C++杂讲 c++对c的扩展

目录

知识点1【c++对c的扩展之::(作用域)】

1、作用域运算 ::

知识点2【c++对c的扩展之命名空间】

1、命名空间的定义

2、命名空间只能全局范围内定义(以下错误写法)

3、命名空间可嵌套命名空间

 4、命名空间是开放的,即可以随时把新的成员加入已有的命名空间中

 5、命名空间 可以存放 变量 和 函数

 6、声明和实现可分离(了解)

 7、无名命名空间

8、命名空间别名

 9、using 使用命名空间

(1)、简化了从命名空间的成员访问

(2)、using 使用整个命名空间

(3)、using 指明使用具体的命名空间 成员

 (4)、using声明碰到函数重载

10、不同命名空间中的 同名成员 使用的时候注意 二义性

 11、using 是修饰 整个命名空间

知识点3【c++对c的扩展之全局变量检测增强】

 知识点4【结构体增强】

1、c++中使用结构体类型的时候 可以不加struct

2、c++中结构体成员 可以是函数

知识点5【新增bool类型】

 知识点6【三目运算符功能增强】

1、C语言的3目运算符 a>b ? a:b

2、C++语言的3目运算符 a>b ? a:b

知识点7【const增强】

1、c++中 如果使用常量 初始化const修饰变量 那么该变量就是符号常量

 2、如果以普通变量 初始化 const修饰的变量 会立即开辟空间 (就不会有符号常量表)

 3、const修饰全局变量a 如果用常量初始化 那么a被分配到文字常量区

 4、为啥尽量以const替换#define

(1)、宏没有类型 const有

(2)、宏的作用域是整个文件 const的作用域 以定义情况决定

(3)、宏不能作为命名空间的成员 const可以

总结:c++总结 

知识点8【引用】

1、定义步骤:

2、引用必须初始化

3、引用一旦确定是谁的别名 就不能更改

4、引用作为函数的参数 可以替代指针变量

5、常引用

6、引用作为函数的返回值类型

(1)、通过函数返回值 在外界操作 函数内部申请的空间

(2)、引用作为函数的返回值类型 可以完成链式操作

 7、引用的本质

8、指针的引用(了解)

知识点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 = &num;
//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;
}

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值