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. 名空间可以是匿名的
```cpp
namespace
{
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声明
```cpp
namespace 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;
}
/*
1
3
*/
注意:使用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++可以放函数,创建结构体变量,可以简化关键字 struct
struct 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 = 100
printf("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 = 100
printf("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++中的一个关键字,是一个限定符,它用来限定一个变量不允许改变,它将一个对象转换成一个常量。
```cpp
const 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值。
```c
const 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分配内存。
```cpp
int 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;
}
运行结果: