1. :: 作用域运算符
通常情况下,如果有两个同名变量,一个是全局变量,另一个是局部变量,那么局部变量在其作用域内具有较高的优先权,它将屏蔽全局变量。
//全局变量int a = 10;void test(){//局部变量int a = 20;//全局a被隐藏cout << "a:" << a << endl;}
程序的输出结果是a:20。在test函数的输出语句中,使用的变量a是test函数内定义的局部变量,因此输出的结果为局部变量a的值。
作用域运算符可以用来解决局部变量与全局变量的重名问题
int atk = 1000;void test01(){int atk = 2000;std::cout << "atk = " << atk << std::endl;// ::代表作用域 如果前面什么都不添加 代表全局作用域std::cout << "全局 atk = " << ::atk << std::endl;}

作用域运算符可以用来解决局部变量与全局变量的重名问题,即在局部变量的作用域内,可用::对被屏蔽的同名的全局变量进行访问。
2. 名字控制
2.1 C++命名空间(namespace)
在c++中,名称(name)可以是符号常量、变量、函数、结构、枚举、类和对象等等。工程越大,名称互相冲突性的可能性越大。另外使用多个厂商的类库时,也可能导致名称冲突。为了避免,在大规模程序的设计中,以及在程序员使用各种各样的C++库时,这些标识符的命名发生冲突,标准C++引入关键字namespace(命名空间/名字空间/名称空间),可以更好地控制标识符的作用域。
2.2 命名空间使用语法
命名空间用途: 解决名称冲突
void test01(){KingGlory::goAtk();LOL::goAtk();}
命名空间下 可以放 变量、函数、结构体、类…
namespace A{int m_A;void func();struct Person{};class Animal{};}
命名空间 必须要声明在全局作用域下
void test02(){//namespace B{}; 不可以命名到局部作用域}
命名空间可以嵌套命名空间
namespace B{int m_A = 10;namespace C{int m_A = 20;}}void test03(){cout << "B空间下的m_A = " << B::m_A << endl;cout << "C空间下的m_A = " << B::C::m_A << endl;}

命名空间是开放的,可以随时给命名空间添加新的成员 ```cpp namespace B { int m_A = 10; namespace C {
int m_A = 20;
} }
namespace B { int m_B = 100; } void test04() { cout << “B空间下的m_A = “ << B::m_A << endl; cout << “B空间下的m_B = “ << B::m_B << endl; }
6. 名空间可以是匿名的```cppnamespace{int m_C = 1000;int m_D = 2000;// 当写的命名空间的匿名的,相当于写了// static int m_C = 1000; static int m_D = 2000;}void test05(){cout << "m_C = " << m_C << endl;cout << "m_D = " << ::m_D << endl;}

- 命名空间可以起别名
```cpp
namespace veryLongName
{
int m_E = 10000;
void func()
{
} } void test06() { namespace veryShortName = veryLongName; cout << veryShortName::m_E << endl; cout << veryLongName::m_E << endl;cout << "aaa" << endl;
}
<a name="SmJEp"></a>### 2.3 using声明1. using声明```cppnamespace KingGlory{int sunwukongId = 1;}void test01(){int sunwukongId = 2;//1、using声明//using KingGlory::sunwukongId ;//当using声明与 就近原则同时出现,出错,尽量避免cout << sunwukongId << endl;// 2}
- using声明碰到函数重载
如果命名空间包含一组用相同名字重载的函数,using声明就声明了这个重载函数的所有集合。namespace A {void func() {}void func(int x) {}void func(int x, int y) {}}void test() {using A::func;func();func(10);func(10, 20);}
2.4 using编译指令
namespace KingGlory{int sunwukongId = 1;}namespace LOL{int sunwukongId = 3;}void test02(){//int sunwukongId = 2;//2、using编译指令using namespace KingGlory;using namespace LOL;//当using编译指令 与 就近原则同时出现,优先使用就近//当using编译指令有多个,需要加作用域 区分cout << KingGlory::sunwukongId << endl;cout << LOL::sunwukongId << endl;}/*13*/
注意:使用using声明或using编译指令会增加命名冲突的可能性。也就是说,如果有名称空间,并在代码中使用作用域解析运算符,则不会出现二义性。
当引入一个全局的using编译指令时,就为该文件打开了该命名空间,它不会影响任何其他的文件,所以可以在每一个实现文件中调整对命名空间的控制。比如,如果发现某一个实现文件中有太多的using指令而产生的命名冲突,就要对该文件做个简单的改变,通过明确的限定或者using声明来消除名字冲突,这样不需要修改其他的实现文件。
3. C++增强于C的地方
3.1 全局变量检测增强
//1、全局变量检测增强int a;int a = 10;
//1、全局变量检测增强 C++检测出重定义int a;//int a = 10;
3.2 C++中所有的变量和函数都必须有类型
返回值、形参类型、函数调用参数个数都要检查
//2、函数检测增强 返回值没有检测 形参类型没有检测 函数调用参数个数没有检测getRectS( w , h){return w *h;}void test01(){printf("%d\n", getRectS(10, 10, 10));}
//2、函数检测增强 返回值检测、形参类型检测、函数调用参数个数int getRectS(int w,int h){return w *h;}void test01(){printf("%d\n", getRectS(10, 10));}
3.3 更严格的类型转换
在C++,不同类型的变量一般是不能直接赋值的,需要相应的强转。
void test02(){char * p = malloc(64);}
//3、类型转换检测增强void test02(){char * p = (char *)malloc(64);}
3.4 struct类型加强
- C中定义结构体变量需要加上
struct关键字,c++不需要。 - C中的结构体只能定义成员变量,不能定义成员函数。c++即可以定义成员变量,也可以定义成员函数。 ```c //4、struct增强 struct Person { int age; //void func(); C语言下 结构体不可以有函数 };
void test03() { struct Person p; //创建结构体变量时候,必须加关键字struct p.age = 100; }
```cpp//4、struct增强 C++可以放函数,创建结构体变量,可以简化关键字 structstruct Person{int age;void func(){age++;}};void test03(){Person p;p.age = 17;p.func();cout << "p的age = " << p.age << endl;}
3.5 三目运算符功能增强
c语言三目运算表达式返回值为数据值,为右值,不能赋值
//6、三目运算符增强void test04(){//?:int a = 10;int b = 20;printf("ret = %d\n", a > b ? a : b);*(a > b ? &a : &b) = 100; //C语言下 返回的是右值 20 = 100printf("a = %d\n", a);printf("b = %d\n", b);}
//6、三目运算符增强void test05(){//?:int a = 10;int b = 20;printf("ret = %d\n", a > b ? a : b);(a < b ? a : b )= 100; // C++下返回的是变量 b = 100printf("a = %d\n", a);printf("b = %d\n", b);}
4 bool 类型关键字
标准c++的bool类型有两种内建的常量true(转换为整数1)和false(转换为整数0)表示状态。这三个名字都是关键字。
- bool类型只有两个值,true(1值),false(0值)
- bool类型占1个字节大小
- 给bool类型赋值时,非0值会自动转换为true(1),0值会自动转换false(0)
```cpp //5、bool类型扩展 C语言下 没有这个类型 C++有bool类型 bool flag = true; // bool类型 代表 真和假 true —— 真(1) false —— 假(0)/5、bool类型扩展 C语言下 没有这个类型//bool a;
void test04() { cout << sizeof(bool) << endl; //结果是1个字节 //flag = false; //flag = 100; //将非0的数都转为1 cout << flag << endl; }
<a name="K75c0"></a>## 5 C/C++中的const<a name="TLyUi"></a>### 5.1 const概述const单词字面意思为常数,不变的。它是c/c++中的一个关键字,是一个限定符,它用来限定一个变量不允许改变,它将一个对象转换成一个常量。```cppconst int a = 10;A = 100; //编译错误,const是一个常量,不可修改
5.2 C/C++中const的区别
5.2.1 C中的const
常量的引进是在c++早期版本中,当时标准C规范正在制定。那时,尽管C委员会决定在C中引入const,但是,他们c中的const理解为”一个不能改变的普通变量”,也就是认为const应该是一个只读变量,既然是变量那么就会给const分配内存,并且在c中const是一个全局只读变量,c语言中const修饰的只读变量是外部连接的。
const int arrSize = 10;int arr[arrSize];
看似是一件合理的编码,但是这将得出一个错误。 因为arrSize占用某块内存,所以C编译器不知道它在编译时的值是多少
5.2.2 C++中的const
在c++中,一个const不必创建内存空间,而在C中,一个const总是需要一块内存空间。在c++中,是否为const常量分配内存空间依赖于如何使用。一般说来,如果一个const仅仅用来把一个名字用一个值代替(就像使用#define一样),那么该存储局空间就不必创建。
如果存储空间没有分配内存的话,在进行完数据类型检查后,为了代码更加有效,值也许会折叠到代码中。
不过,取一个const地址, 或者把它定义为extern,则会为该const创建内存空间。
在c++中,出现在所有函数之外的const作用于整个文件(也就是说它在该文件外不可见),默认为内部连接,c++中其他的标识符一般默认为外部连接。
5.2.3 C/C++中const异同总结
- c语言全局const会被存储到只读数据段。c++中全局const当声明extern或者对变量取地址时,编译器会分配存储地址,变量存储在只读数据段。两个都受到了只读数据段的保护,不可修改。 ```c const int constA = 10;
int main() { int p = (int)&constA; *p = 200; }
以上代码在c/c++中编译通过,在运行期,修改constA的值时,发生写入错误。原因是修改只读数据段的数据。2. c语言中局部const存储在堆栈区,只是不能通过变量直接修改const只读变量的值,但是可以跳过编译器的检查,通过指针间接修改const值。```cconst int constA = 10;int* p = (int*)&constA;*p = 300;printf("constA:%d\n",constA);printf("*p:%d\n", *p);/*constA:300*p:300*/
c语言中,通过指针间接赋值修改了constA的值。
c++中对于局部的const变量要区别对待:
- 对于基础数据类型,也就是
const int a = 10这种,编译器会把它放到符号表中,不分配内存,当对其取地址时,会分配内存。 ```cpp const int constA = 10; int p = (int)&constA; p = 300; cout << “constA:” << constA << endl; cout << “p:” << *p << endl;
/运行结果: constA:10 p:300 */
constA在符号表中,当我们对constA取地址,这个时候为constA分配了新的空间,*p操作的是分配的空间,而constA是从符号表获得的值。2. 对于基础数据类型,如果用一个变量初始化const变量,如果`const int a = b`,那么也是会给a分配内存。```cppint b = 10;const int constA = b;int* p = (int*)&constA;*p = 300;cout << "constA:" << constA << endl;cout << "*p:" << *p << endl;/*运行结果:constA:300*p:300*/
constA 分配了内存,所以我们可以修改constA内存中的值。
- 对于自定数据类型,比如类对象,那么也会分配内存。 ```cpp const Person person; //未初始化age //person.age = 50; //不可修改 Person pPerson = (Person)&person; //指针间接修改 pPerson->age = 100; cout << “pPerson->age:” << pPerson->age << endl; pPerson->age = 200; cout << “pPerson->age:” << pPerson->age << endl;
/运行结果: pPerson->age:100 pPerson->age:200 /
为person分配了内存,所以我们可以通过指针的间接赋值修改person对象。**C中const默认为外部连接,c++中const默认为内部连接**.当c语言两个文件中都有`const int a`的时候,编译器会报重定义的错误。而在c++中,则不会,因为c++中的const默认是内部连接的。如果想让c++中的const具有外部连接,必须显示声明为: `extern const int a = 10`;<a name="rMKq6"></a>### 5.3 尽量以const替换#define在旧版本C中,如果想建立一个常量,必须使用预处理器”<br />`#define MAX 1024;`<br />我们定义的宏MAX从未被编译器看到过,因为在预处理阶段,所有的MAX已经被替换为了1024,于是MAX并没有将其加入到符号表中。但我们使用这个常量获得一个编译错误信息时,可能会带来一些困惑,因为这个信息可能会提到1024,但是并没有提到MAX.如果MAX被定义在一个不是你写的头文件中,你可能并不知道1024代表什么,也许解决这个问题要花费很长时间。解决办法就是用一个常量替换上面的宏。<br />`const int max= 1024;``const`和`#define`区别总结:1. const有类型,可进行编译器类型安全检查。#define无类型,不可进行类型检查.1. const有作用域,而#define不重视作用域,默认定义处到文件结尾.如果定义在指定作用域下有效的常量,那么#define就不能用。<a name="emFqv"></a>## 6. new操作符C++中利用**new**操作符在堆区开辟数据<br />堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符**delete**语法:`ElementType* VariableName = new ElementType([InitialValue])`<br />数组:`ElementType* ArrayName = new ElementType[ArrayLength]`删除释放数组时语法为:`delete[] ArrayName`<br />利用new创建的数据,会返回该数据对应的类型的指针**示例1: 基本语法**```cpp#include<iostream>using namespace std;int* func(){int* a = new int(10);return a;}int main() {int* p = func();cout << *p << endl;cout << *p << endl;//利用delete释放堆区数据delete p;//cout << *p << endl; //报错,释放的空间不可访问return 0;}
运行结果:
示例2:开辟数组
#include<iostream>using namespace std;//堆区开辟数组int main() {int* arr = new int[10];//为数组赋值for (int i = 0; i < 10; i++){arr[i] = i + 100;}//输出数组里的值for (int i = 0; i < 10; i++){cout << arr[i] << endl;}//释放数组 delete 后加 []delete[] arr;return 0;}
运行结果:
