美文网首页C++
C++语法系列之4

C++语法系列之4

作者: hello12qwerz | 来源:发表于2018-03-25 11:16 被阅读0次
    泰山之霤穿石,单极之绠断干。
    水非石之钻,索非木之锯,渐靡使之然也。
    --诸君共勉。
    

    1 复制构造函数的问题

    如果没有显示的编写复制构造函数或者赋值运算符,编译器会自动生成默认的复制构造函数和赋值运算符。
    如果有指针成员变量,那么默认的复制构造函数和赋值运算符,默认操作是拷贝指针对应的值,不会拷贝指针指向的底层数据(也即浅拷贝)。这样复制出来的对象都会指向同一个内存区域。有些时候是所需要的功能,但是有些时候,就会出现问题:

    1. 一个对象销毁以后,对应的内存区域就会被回收,但是另一个对象可能还指向该内存区域,成为野指针/悬挂指针);
    2. 对象a被赋予b的值以后,对象a原有指针指向的内存区域被丢弃,此时没有调用回收方法,会导致内存泄漏。

    为了避免上述问题,复制构造函数和赋值运算符,必须执行深拷贝。
    由于复制构造函数和赋值运算符执行的时候,前者数据成员并不存在,后者只是修改已存在的数据成员的值。所以两者会有一些差异。区别如下:
    复制构造函数:
    1)为数据成员分配内存;
    2)执行深拷贝;
    赋值运算符:
    1)释放数据成员内存;
    2)分配新的内存;
    3)执行深拷贝;

    由此可知:析构函数,复制构造函数,赋值运算符,只要编写了其中一个,就必须编写所有这三个方法。

    2 禁止赋值和按值传递

    在类中动态分配内存的时候,如果想禁止复制对象和为对象赋值,可以如下操作:
    1)可以显示的将operator=和复制构造函数标记为delete即可。必须要提供delete复制构造函数和赋值运算符的实现。(因为编译器不允许代码调用这两者,所以链接器永远也不会查看它们)。

    1. 可以把复制构造函数和赋值运算符标记为private,且不需要提供任何实现。(如果编译器不支持显示的delete的话)

    3 静态数据成员(static)

    静态数据成员属于类而不是对象的数据成员。可将静态数据成员当做类的全局变量。
    注意:
    1)需要在类定义中列出static 类成员;

    1. 还需要在源文件中为其分配内存(也就是定义类方法的文件中)
      3)静态数据成员和普通的变量/数据成员不同:静态数据成员默认会初始化为0或者nullptr,而不是随机值。
      4)使用时,类外必须使用类名::静态变量的形式;类中可以像普通成员变量一样使用。
    class A {
       public:
        static int sCounter;//1定义static类成员
    }
    
    int A::sCounter; //2源文件中分配内存,
                             //3默认初始化为0
    
    A::sCounter = 9;//4通过类名::静态变量的形式使用
    

    3 常量数据成员(const)

    const可以修饰两种情况:基本数据变量,和指针。

    const int a = 20;//a只读变量
    int const b = 20;//和上面等价,b只读变量
    b = 1;//error,不允许修改常量值
    
    const int *p;//*p为常量;p为变量
    int const *p1;// 和上面等价
    
    int * const p2;//*p2为变量,p2为常量
    
    const int * const p3;//*p3为常量;p3为常量
    int const * const p4;//和上面等价
    

    对于const常量,要么在类定义中定义的时候马上初始化,要么使用构造函数初始化器初始化。

    //examle 1:类定义中定义const常量的时候马上初始化
    class A  {
    public:
       const double kDouble = 1.0;
    }
    
    //example 2:使用构造函数初始器初始化
    class A {
    public:
        const double kDouble;
    }
    
    A::A() :kDouble(1.0) {
    
    }
    
    

    特别注意:
    如果在定义的时候,没有初始化const常量,想在构造函数中初始化会报错。

    4 诡异的static const

    class A {
       public:
        static const int a = 1;//编译正确
        static const double b = 1.0;//编译错误,static常量只有int才可以直接在类定义中初始化,其余均需要在实现函数或者方法的源文件中初始化
    }
    
    class A {
       public:
         static const double b;//定义变量,可以正确编译
    }
    
    const double A::b = 3.0;//初始化,注意:这里要写const,同时不要写static,static只会出现在类定义中
    

    5 引用数据成员

    如果两个类互相引用,此时两个类必须互相知道对方。此时会有一个循环引用问题。普通的#include头文件的方式无法解决。
    解决方案:在其中一个类的头文件中使用前置声明。

    class B;
    class A {
    public:
       A(int a, B& b);
     protected:
       B& mB;
       int ma;
    }
    
    A::A(int a, B&b):ma(a),mB(b) {
    
    }
    
    A::A(const A&a):mB(a.mB) {
       ma = a;
    }
    

    说明:
    1)以上类A引用类B,需要在类A的头文件中前置声明类B;
    2)A的构造函数初始化的时候,必须使用构造函数初始化器初始化引用mB;
    3)A的复制构造函数也必须使用构造函数初始化器初始化引用mB;

    6 常量引用数据成员

    常量引用和非常量引用的重要差别在于:
    常量引用数据成员只能用于调用对应的常量方法;
    不能通过常量引用调用非常量方法;
    所以:建议将不改变数据成员的方法都标记为const(也即常量方法)

    7 static方法

    类的方法。
    static方法只可以访问static数据成员,不能访问非static数据成员。

    7 const方法

    将方法标记为const相当于与客户代码定下契约,承诺不会在方法中改变对象内部的值。其原理为编译器将方法内部所有用到的数据成员都标记为const,一旦尝试修改数据成员,编译器就会报错。

    8 mutable数据成员

    有些方法虽然是逻辑上的const,但是还是会改变对象的数据成员,这个改动对用户可见的数据没有任何影响(比如统计方法调用次数)。
    此时由于const方法的限制,编译器会报错。要解决该问题,可以将成员变量标记为mutable

    9 方法重载

    可以根据const重载方法:也即可以编写两个名称完全相同,参数完全相同的方法,一个为const,一个不是。如果是const对象,就只能调用const方法;如果是非const对象,就调用非const方法

    10 默认参数

    带有默认参数的方法,不应与重载的方法导致混淆。
    比如:如果提供了一个默认无参构造函数。同时由提供了一个全部参数都由默认值的构造函数。那么此时由于会有歧义,编译器会报错。

    11 inline方法

    inline方法具体是否会内联,与编译器的判断也有关系。

    12 嵌套类

    C++中嵌套类没有Java那么方便,使用相对也少。

    相关文章

      网友评论

        本文标题:C++语法系列之4

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