美文网首页
C++ 对 C的扩展 (1)

C++ 对 C的扩展 (1)

作者: maskerII | 来源:发表于2022-06-16 21:33 被阅读0次

    1. ::作用域运算符

    作用域云算符 用来访问某个作用域里面的成员
    :: 前无命名空间名 表示访问全局的
    :: 前有命名空间名 表示访问该命名空间的成员

    通常情况下,如果有两个同名变量,一个是全局变量,另一个是局部变量,那么局部变量在其作用域内具有较高的优先权,它将屏蔽全局变量

    //全局变量
    int a = 10;
    void test(){
        //局部变量
        int a = 20;
        //全局a被隐藏
        cout << "a:" << a << endl;
    }
    

    程序的输出结果是a:20。在test函数的输出语句中,使用的变量a是test函数内定义的局部变量,因此输出的结果为局部变量a的值。

    作用域运算符可以用来解决局部变量与全局变量的重名问题

    /全局变量
    int a = 10;
    //1. 局部变量和全局变量同名
    void test(){
        int a = 20;
        //打印局部变量a
        cout << "局部变量a:" << a << endl;
        //打印全局变量a
        cout << "全局变量a:" << ::a << endl;
    }
    

    这个例子可以看出,作用域运算符可以用来解决局部变量与全局变量的重名问题,即在局部变量的作用域内,可用::对被屏蔽的同名的全局变量进行访问。

    2. 命名空间

    2.1 命名空间使用语法

    创建一个命名空间:

    namespace A{
        int a = 10;
    }
    namespace B{
        int a = 20;
    }
    void test(){
        cout << "A::a : " << A::a << endl;
        cout << "B::a : " << B::a << endl;
    }
    

    命名空间只能全局范围内定义

    // ERROR
    void test()
    {
        // 错误
        namespace A{
            int a = 10;
        }
        namespace B{
            int a = 20;
        }
        cout << "A::a : " << A::a << endl;
        cout << "B::a : " << B::a << endl;
    }
    

    命名空间可嵌套命名空间

    namespace A{
        int a = 10;
        namespace B{
            int a = 20;
        }
    }
    void test(){
        cout << "A::a : " << A::a << endl;
        cout << "A::B::a : " << A::B::a << endl;
    }
    

    命名空间是开放的,即可以随时把新的成员加入已有的命名空间中,但新成员只能在加入后使用

    namespace A{
        int a = 10;
    }
    
    namespace A{
        void func(){
            cout << "hello namespace!" << endl;
        }
    }
    
    void test(){
        cout << "A::a : " << A::a << endl;
        A::func();
    }
    

    2.2 using声明

    using声明可使得指定的标识符可用

    namespace A{
        int paramA = 20;
        int paramB = 30;
        void funcA(){ cout << "hello funcA" << endl; }
        void funcB(){ cout << "hello funcA" << endl; }
    }
    
    void test(){
        //1. 通过命名空间域运算符
        cout << A::paramA << endl;
        A::funcA();
        //2. using声明
        using A::paramA; 
        using A::funcA;
        cout << paramA << endl;
        //cout << paramB << endl; //不可直接访问
        funcA();
        //3. 同名冲突
        using A::paramB; // using声明相当于把A::paramB 在此处重新定义
        //int paramB = 20; // 注意:using声明了某个变量,在该作用域内不能再定义同名变量
    }
    

    using声明碰到函数重载

    namespace A{
        void func(){}
        void func(int x){}
        int  func(int x,int y){}
    }
    void test(){
        using A::func;
        func();
        func(10);
        func(10, 20);
    }
    

    如果命名空间包含一组用相同名字重载的函数,using声明就声明了这个重载函数的所有集合。

    2.3 using编译指令

    using编译指令使整个命名空间标识符可用

    
    namespace E{
        int paramA = 20;
        void funcA(){ cout << "hello funcA" << endl; }
    }
    
    void test04(){
        using namespace E;
        cout << paramA << endl;
        funcA();
    
        //不会产生二义性
        int paramA = 30;
        cout << paramA << endl;
    }
    
    namespace F{
        int paramA = 20;
        void funcA(){ cout << "hello funcA" << endl; }
    }
    
    void test05(){
        using namespace E;
        using namespace F;
        //二义性产生,不知道调用A还是B的paramA
        //cout << paramA << endl;
        
        cout << E::paramA << endl; // OK
    }
    

    注意:使用using声明或using编译指令会增加命名冲突的可能性。可在代码中使用作用域运算符来避免产生二义性

    3. struct类型加强

    • c中定义结构体变量需要加上struct关键字,c++不需要
    • c中的结构体只能定义成员变量,不能定义成员函数。c++即可以定义成员变量,也可以定义成员函数。
    //1. 结构体中即可以定义成员变量,也可以定义成员函数
    struct Student{
        string mName;
        int mAge;
        void setName(string name){ mName = name; }
        void setAge(int age){ mAge = age; }
        void showStudent(){
            cout << "Name:" << mName << " Age:" << mAge << endl;
        }
    };
    
    //2. c++中定义结构体变量不需要加struct关键字
    void test01(){
        Student student;
        student.setName("John");
        student.setAge(20);
        student.showStudent();
    }
    

    4. 更严格的类型转换

    在C++,不同类型的变量一般是不能直接赋值的,需要相应的强转

    void test()
    {
        // 不能隐性转换,必须显性转换
        char *p = (char *)malloc(64);
        printf("%p", p);
    }
    

    5. 三目运算符功能增强

    c语言三目运算表达式返回值为数据值,为右值,不能赋值

    void test() 
    {
        int a = 10;
        int b = 20;
    
        // C 语言的三目运算符是右值
        *(a > b ? &a : &b) = 100;
        printf("%d \n",b);
    }
    

    c++语言三目运算表达式返回值为变量本身(引用),为左值,可以赋值

    void test() 
    {
        int a = 10;
        int b = 20;
        cout << (a>b?a:b) << endl;
    
         // C++ 语言的三目运算符是左值
        (a>b?a:b) = 100;
        cout << b << endl;
    }
    

    [左值和右值概念]
    在c++中可以放在赋值操作符左边的是左值,可以放到赋值操作符右面的是右值。
    有些变量即可以当左值,也可以当右值。
    左值为Lvalue,L代表Location,表示内存可以寻址,可以赋值。
    右值为Rvalue,R代表Read,就是可以知道它的值。
    比如:int temp = 10; temp在内存中有地址,10没有,但是可以Read到它的值。

    6. C/C++中的const

    6.1 C 语言的const

    • C语言的const修饰的变量都有空间
    • C语言的const修饰的全局变量具有外部链接属性
    // 1.C语言的const修饰的变量都有空间
    // 2.C语言的const修饰的全局变量具有外部链接属性 
    const int a = 10; // 常量区,一旦初始化,不能修改
    void test01() {
        // a = 200; // 全局的const不能直接修改
        int *p = (int *)&a;
       // *p = 100; // 全局的const不能间接修改
        printf("%d \n",a);
    }
    
    void test02() {
        const int b = 30; // 栈区
        // b = 200; // 局部的const修饰的变量不能直接修改
        int *p = (int *)&b;
        *p = 300; // 局部的const修饰的变量可以间接修改
        printf("b的地址 %p %d\n",&b,b);
        printf("p指向的地址 %p %d \n",p,*p);
    }
    

    6.2 C++语言的const

    • C++ 语言的const修饰的变量有时有空间,有时没有空间
    • C++ 语言中const修饰的全局变量具有内部链接属性

    c语言全局const会被存储到只读数据段。c++中全局const当声明extern或者对变量取地址时,编译器会分配存储地址,变量存储在只读数据段。

    1.对于基础数据类型,也就是const int a = 10这种,编译器会进行优化,将值替换到访问的位置。

    // 1.对于基础数据类型,也就是const int a = 10这种,编译器会进行优化,将值替换到访问的位置
    void test03()
    {
        const int constA = 10;
        int* p = (int*)&constA;
        *p = 300;
        // 发生常量折叠 在编译阶段 编译器:cout << "constA:" << 10 << endl
        cout << "constA:" << constA << endl; // 10 
        
        cout << "*p:" << *p << endl; // 30
    }
    
    1. 对于基础数据类型,如果用一个变量初始化const变量,如果const int a = b,那么也是会给a分配内存。
    
    // 2.对于基础数据类型,如果用一个变量初始化const变量,如果const int a = b,那么也是会给a分配内存。
    void test04() 
    {
        int b = 10;
        const int constA = b;
        int* p = (int*)&constA;
        *p = 300;
        cout << "constA:" << constA << endl; // 300
        cout << "*p:" << *p << endl; // 300
    }
    
    
    1. 对于自定数据类型,比如类对象,那么也会分配内存。
    class Person
    {
    public:
        Person(){
    
        }
    
    public:
        int age;
    };
    void test05()
    {
        const Person person; //未初始化age
        //person.age = 50; //不可修改
        Person* pPerson = (Person*)&person;
        //指针间接修改
        pPerson->age = 100;
        cout << "pPerson->age:" << pPerson->age << endl; // 100
        pPerson->age = 200;
        cout << "pPerson->age:" << pPerson->age << endl; // 200
    }
    

    c中const默认为外部连接,c++中const默认为内部连接.当c语言两个文件中都有const int a的时候,编译器会报重定义的错误。而在c++中,则不会,因为c++中的const默认是内部连接的。如果想让c++中的const具有外部连接,必须显示声明为: extern const int a = 10;

    6.3 尽量以const替换#define

    1.const 有类型,可进行编译器类型安全检查,#define 无类型,不可进行类型检查

    #define MA 128
    const short ma = 128;
    
    void func(short a){
        cout << "func(short a)" << endl;
    }
    
    void func(int a){
        cout << "func(int a)" << endl;
    }
    
    void test01() {
        func(MA); // func(int a)
        func(ma); // func(short a)
    }
    

    2.const 有作用域,而#define 不重视作用域,默认定义处到文件结尾,不能限定常量的使用范围,如果定义在指定作用域下有效的常量,那么#define就不能用

    #define b 10
    void test() {
       const int a = 10;
       cout << a << endl; // 只能在test函数内访问a
    }
    
    void test02() {
        test();
        cout << b << endl; 
        // cout << a << endl; a 不可以访问
    }
    

    相关文章

      网友评论

          本文标题:C++ 对 C的扩展 (1)

          本文链接:https://www.haomeiwen.com/subject/lhaqvrtx.html