目录
4、命名空间是开放的,即可以随时把新的成员加入已有的命名空间中
1、c++中 如果使用常量 初始化const修饰变量 那么该变量就是符号常量
2、如果以普通变量 初始化 const修饰的变量 会立即开辟空间 (就不会有符号常量表)
3、const修饰全局变量a 如果用常量初始化 那么a被分配到文字常量区
(2)、宏的作用域是整个文件 const的作用域 以定义情况决定
知识点1【c++对c的扩展之::(作用域)】
1、作用域运算 ::
表明 数据、方法 的归属性问题(解决C语言的 归属问题)
如果局部和全局同名,可以通过作用域表明区分两者,可以直接访问全局变量
using namespace std;
int a = 10;//全局变量
void test01()
{
int a = 20;//局部变量
cout<<"局部变量a = "<<a<<endl;//优先选择局部变量
//::作用域运算符(c++独有)
cout<<"全局变量a = "<<::a<<endl;//取全局变量
}
运行结果:
知识点2【c++对c的扩展之命名空间】
命名空间 namespace 解决命名冲突
1、命名空间的定义
namespace 命名空间名
{
//打包的数据 和 函数
}
//定义一个名字为A的命名空间(变量、函数)
namespace A {
int a = 100;
}
namespace B {
int a = 200;
}
void test02()
{
//A::a a是属于A中
cout<<"A中a = "<<A::a<<endl;//100
cout<<"B中a = "<<B::a<<endl;//200
}
2、命名空间只能全局范围内定义(以下错误写法)
3、命名空间可嵌套命名空间
namespace A {
int a = 1000;
namespace B {
int a = 2000;
}
}
void test03()
{
cout<<"A中的a = "<<A::a<<endl; //1000
cout<<"B中的a = "<<A::B::a<<endl; //2000
}
4、命名空间是开放的,即可以随时把新的成员加入已有的命名空间中
namespace A {
int a = 100;
int b = 200;
}
//将c添加到已有的命名空间A中
namespace A {
int c = 300;
}
void test04()
{
cout<<"A中a = "<<A::a<<endl;//100
cout<<"A中c = "<<A::c<<endl;//200
}
5、命名空间 可以存放 变量 和 函数
namespace A {
int a=100;//变量
void func()//函数
{
cout<<"func遍历a = "<<a<<endl;
}
}
void test05()
{
//变量的使用
cout<<"A中的a = "<<A::a<<endl;
//函数的使用
A::func();
}
6、声明和实现可分离(了解)
命名空间中的函数 可以在“命名空间”外 定义
namespace C {
int a = 10;
void set_a(int data);
int get_a(void);
}
void C::set_a(int data)
{
a = data;
return;
}
int C::get_a(void)
{
return a;
}
void test03()
{
C::set_a(100);
cout<<"a = "<<C::get_a()<<endl;
}
7、无名命名空间
无名命名空间里面的数据和方法 只能在当前源文件使用 就类似加static修饰
无名命名空间,意味着命名空间中的标识符只能在本文件内访问,相当于给这个标识符加上了static,使得其可以作为内部连接
内部连接: 只在当前源文件用
外部连接:可以在其他源文件使用
8、命名空间别名
namespace veryLongName{
int a = 10;
void func(){ cout << "hello namespace" << endl; }
}
void test(){
namespace shortName = veryLongName;
cout << "veryLongName::a : " << shortName::a << endl;
veryLongName::func();
shortName::func();
}
9、using 使用命名空间
(1)、简化了从命名空间的成员访问
namespace veryLongName {
int a=100;
void func(){cout<<"hello namespace"<<endl;}
}
void test07()
{
//使用veryLongName命名空间
using namespace veryLongName;
//出现的变量 从veryLongName命名空间中找 找不到 从其他地方中
cout<<"a = "<<a<<endl;
func();
}
(2)、using 使用整个命名空间
namespace veryLongName {
int a=100;
void func(){cout<<"hello namespace"<<endl;}
}
void test07()
{
int a=200;
//使用veryLongName命名空间
using namespace veryLongName;
//出现的变量 从veryLongName命名空间中找 找不到 从其他地方中
cout<<"a = "<<a<<endl;//访问的是局部变量中的a
cout<<"a = "<<veryLongName::a<<endl;//访问的是veryLongName的a
func();
}
(3)、using 指明使用具体的命名空间 成员
using直接使用 命名空间中的成员 不会和 全局变量冲突
namespace veryLongName {
int a=100;
void func(){cout<<"hello namespace"<<endl;}
}
int a = 200;
void test07()
{
//using直接使用 命名空间中的成员 不会和 全局变量冲突
using veryLongName::a;
cout<<"命名空间中a = "<<a<<endl;//命名空间中的成员 100
cout<<"全局变量中a = "<<::a<<endl;//200
//但是func使用的时候 必须加作用域
veryLongName::func();
}
(4)、using声明碰到函数重载
如果命名空间包含一组用相同名字重载的函数,using声明就声明了这个重载函数的所有集合
namespace A {
//函数重载 函数名+参数 组合代表是函数的入口地址
void func(){cout<<" 无参的func"<<endl;}
void func(int a){cout<<" int的func"<<endl;}
void func(int a,int b){cout<<" int int的func"<<endl;}
}
void test08()
{
//using指明 使用 A中的func 会对 所有的func起作用
using A::func;
func();
func(10);
func(10,20);
}
10、不同命名空间中的 同名成员 使用的时候注意 二义性
namespace A {
int a = 10;
}
namespace B {
int a = 20;
}
void test09()
{
//此处的a 不知道是A还是B中a
//cout<<"a = "<<a<<endl;//err
//解决方法
cout<<"A::a = "<<A::a<<endl;//100
cout<<"B::a = "<<B::a<<endl;//200
}
11、using 是修饰 整个命名空间
namespace D {
int a = 10;
int b = 20;
int c = 30;
}
void test04()
{
//声明识别整个命名空间 里的数据和方法
using namespace D;//强调的是命名空间名
//先查看当前普通变量a 如果没有 再查看命名空间中有没有a
cout<<"a = "<<a<<endl;//10
//不会冲突
int a = 100;
cout<<"a = "<<a<<endl;//100就近原则
}
知识点3【c++对c的扩展之全局变量检测增强】
C++中所有的变量和函数都必须有类型
函数的参数必须有类型
函数没有参数 那么调用的时候 不能传实参
知识点4【结构体增强】
1、c++中使用结构体类型的时候 可以不加struct
struct Stu{};
Stu lucy;
2、c++中结构体成员 可以是函数
struct Stu
{
int num;
void set_num(int data)
{
num = data;
}
};
void test01()
{
Stu lucy;
lucy.set_num(100);
cout<<"num = "<<lucy.num<<endl;
}
知识点5【新增bool类型】
bool的变量只能赋值为true (非0) 或false (0)
void test02()
{
bool mybool;
cout<<"sizeof(bool) = "<<sizeof(bool)<<endl;//1字节
mybool = false;
cout<<"false = "<<false<<endl;//0
cout<<"true = "<<true<<endl;//1
}
知识点6【三目运算符功能增强】
注:
能被赋值的就是左值 不能被赋值的就是右值
1、C语言的3目运算符 a>b ? a:b
c语言三目运算表达式返回值为数据值,为右值,不能赋值
a>b ? a:b返回的是a或b的值。
(a>b ? a:b) = 100;//错误
void test02()
{
int a = 10;
int b = 20;
printf("C语言:%d\n", a>b?a:b);//20
//a>b?a:b整体结果 右值(不能被赋值)
a>b?a:b = 100;//err不能被赋值
}
2、C++语言的3目运算符 a>b ? a:b
c++语言三目运算表达式返回值为变量本身(引用),为左值,可以赋值
a>b ? a:b返回的是a或b的引用(a或b的变量名)
(a>b ? a:b) =100;//ok
void test02()
{
int a = 10;
int b = 20;
cout<<"c++中:"<<(a>b?a:b)<<endl;
//a>b?a:b整体结果是变量本身(引用) 左值(能被赋值)
a>b?a:b = 100;//b =100
}
知识点7【const增强】
C语言中 const修饰的普通变量 是只读变量
在c语言中:
1、const修饰全局变量num 变量名只读 内存空间在文字常量区(只读)、不能通过num的地址 修改空间内容
2、const修饰局部变量data 变量名只读 内存空间栈区(可读可写),可以通过data地址 间接的修改空间内容
const int a = 10;//不要把a看成常量
//a的本质 是变量 只是 只读变量
1、c++中 如果使用常量 初始化const修饰变量 那么该变量就是符号常量
const int num = 10;//num没有空间
num是符号常量 被放入符号常量表中
如果对num取地址 这时候 系统才会为num开辟空间
例程:
fun.cpp:
//const修饰的全局变量 默认是内部链接(只在当前源文件有效 不能直接用于其他源文件)
//const int num = 100;
//如果必须用在其他源文件 使用只读的全局变量 必须加extern将num转换成外部链接
extern const int num = 100;
main.cpp
//声明
extern const int num;
struct Person
{
int num;
char name[32];
};
void test04()
{
cout<<"全局num = "<<num<<endl;//err 不识别num
//1、c++中 对于基础类型 系统不会给data开辟空间 data放到符号表中
const int data = 10;
//data = 100;//err 只读
cout<<"data = "<<data<<endl;
//2、c++中当 对data 取地址的时候 系统就会给data开辟空间
int *p = (int *)&data;
*p = 2000;
cout<<"*p = "<<*p<<endl;//空间内容修改成功 2000
cout<<"data = "<<data<<endl;//data 还是10为啥?
//2、当以变量的形式 初始化 const修饰的变量 系统会为其开辟空间
int b = 200;
const int a= b;//系统直接为a开辟空间 而不会把a放入符号表中
p = (int *)&a;
*p = 3000;
cout<<"*p = "<<*p <<endl;//3000
cout<<"a = "<<a <<endl;//3000
//3、const 自定义数据类型(结构体、对象) 系统会分配空间
const Person per = {100,"lucy"};
//per.num = 1000;//err
cout<<"num = "<<per.num<<", name = "<<per.name<<endl;//100 lucy
Person *p1 = (Person *)&per;
p1->num = 2000;
cout<<"num = "<<per.num<<", name = "<<per.name<<endl;//2000 lucy
}
2、如果以普通变量 初始化 const修饰的变量 会立即开辟空间 (就不会有符号常量表)
int b = 10;
const int a = b;//a立即开辟空间 没有符号常量
3、const修饰全局变量a 如果用常量初始化 那么a被分配到文字常量区
4、为啥尽量以const替换#define
const有类型,可进行编译器类型安全检查。#define无类型,不可进行 类型检查
const有作用域,而#define不重视作用域 #define不能作为类的成员
(1)、宏没有类型 const有
#define MAX 1024
const short my_max =1024;
void func(short i)
{
cout<<"short函数"<<endl;
}
void func(int i)
{
cout<<"int函数"<<endl;
}
void test05()
{
func(MAX);//int 函数
func(my_max);//short函数
}
(2)、宏的作用域是整个文件 const的作用域 以定义情况决定
void my_func(void)
{
//作用范围 是当前复合语句
const int my_num = 10;
//作用范围 当前位置 到文件结束
#define MY_NUM 10
}
void test06()
{
//cout<<"my_num = "<<my_num<<endl;//err 不识别
cout<<"MY_NUM = "<<MY_NUM<<endl;//ok 能识别
}
(3)、宏不能作为命名空间的成员 const可以
总结:c++总结
1、const int data = 10;//data先放入符号表
2、如果对data取地址 系统才会给data开辟空间
3、const int a = b;//b是变量名 系统直接给a开辟空间 而不放入符号表
4、cosnt 修饰自定义数据 系统为自定义数据开辟空间
知识点8【引用】
引用的本质:给已有的变量名 取个别名
//给num取个别名为b
int num =100;
//&不是取b的地址 只是描述b是num的别名 编译器不会为b开辟新的空间
int &b = num;//num的别名 是b
//操作b等价操作num
1、定义步骤:
1、&修饰别名
2、给哪个变量取别名 就定义哪个变量
3、从上往下 整体替换
案例1:给数组取别名
int arr[5] ={1,2,3,4,5};
int (&new_arr)[5] = arr;
2、引用必须初始化
int &b;//非法的
int num = 10;
int &a = num;
int data = 20;
a = data;//不是data别名为a 而是将data值赋值a(num)
3、引用一旦确定是谁的别名 就不能更改
int num = 10;
int &b = num;
int data = 20;
b=data;//千万不要认为是b给data取别名 仅仅是将data的值赋值b也就是data赋值num
4、引用作为函数的参数 可以替代指针变量
void swap_int01(int a1, int b1)
{
int tmp = a1;
a1 = b1;
b1 = tmp;
}
void swap_int02(int *a1, int *b1)//a1=&a, b1=&b
{
//*a1 == a, *b1 == b
int tmp = *a1;
*a1 = *b1;
*b1 = tmp;
}
void swap_int03(int &a1, int &b1)//int &a1 = a, int &b1=b
{
//a1 == a, b1 == b
int tmp = a1;
a1 = b1;
b1 = tmp;
}
void test07()
{
int a = 10;
int b = 20;
cout<<"a = "<<a<<", b = "<<b<<endl;
// swap_int01(a, b);//交换 不成功
// swap_int02(&a, &b);//交换 成功
swap_int03(a, b);//交换 成功
cout<<"a = "<<a<<", b = "<<b<<endl;
}
int main(int argc, char *argv[])
{
test07();
return 0;
}
引用作为函数的参数的好处:
1、函数内部 直接通过引用操作外部变量的值
2、省去了指针的操作
3、函数的形参不会拥有新的空间(节约了空间)
5、常引用
int &a = 10;//err
void test08()
{
//a就叫常引用 不能通过a修改空间的值
const int &a = 10;//ok
cout<<"a = "<<a<<endl;//10
}
常引用 一般作为函数的参数 防止函数内部修改外部空间值
//常引用 作为函数的参数 即节约了空间 又防止函数内部修改外部变量的值
void printf_num(const int &a)
{
//a = 1000;//err
cout<<" num = "<<a<<endl;
}
void test09()
{
int num = 10;
printf_num(num);
}
6、引用作为函数的返回值类型
当函数返回值作为左值 那么函数的返回值类型必须是引用。
(1)、通过函数返回值 在外界操作 函数内部申请的空间
int& get_data(void)
{
static int data = 100;
//不要返回 普通局部变量的 引用
return data;//返回谁 外界的a就给data取别名
}
void test10()
{
int &a = get_data();
cout<<"data = "<<a<<endl;
}
(2)、引用作为函数的返回值类型 可以完成链式操作
7、引用的本质
引用的本质在c++内部实现是一个指针常量. Type& ref = val; // Type* const ref = &val;
c++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同,只是这个过程是编译器内部实现,用户不可见
引用的本质:常量指针变量
int num = 10;
int &b = num;//b == num
//底层实现
//b是只读 *b可读可写
int * const b = #
//b = 100;
*b = 100;
8、指针的引用(了解)
new_p就是指针的引用
指针的引用的场景:
void get_memory01(int **p1)//int **p1 = &p
{
//*p1 == p
*p1 = (int *)calloc(1, sizeof(int));
**p1 = 100;
}
void get_memory02(int* &p1)//int* &p1 = p
{
//p1 == p
p1 = (int *)calloc(1, sizeof(int));
*p1 = 100;//*p1 = *p
}
void test13()
{
int *p = NULL;
//get_memory01(&p);
get_memory02(p);
cout<<"*p = "<<*p<<endl;
}