• C++类的构造、拷贝构造、析构函数等


    1:

    一个空的class在C++编译器处理过后就不再为空,编译器会自动地为我们声明一些member function,如果你写 


    class A{}; 
    编译器处理后,就相当于: 

    class A

    public: 
    A();  //默认构造函数
    A(const A&);  //拷贝构造函数
    ~A();  //析构函数
    A& operator=(const A& rhs); 
    A* operator&();  //取地址运算符

    const A* operator&() const;
    }; 

    基本上符合我上文的回答,只是多了取地址运算符的两个函数。

    这种回答对否?

    其实对于这样的一个空类来说,是完全没有必要的,而编译器也不是这样做的。编译器的做法是:

    只有你需要用到这些函数并且你又没有显示的声明这些函数的时候,编译器才会贴心的自动声明相应的函数。  

    比如   
    A   a;   
    编译器就会根据上面的实例,给类A生成构造函数和析构函数。   
    当使用   
    A   b(b);   
    编译器就会生成类A的拷贝构造函数。   
    A   c;   
    c   =   a;   
    编译器生成赋值运算符函数   
    A   &d   =   a;   
    编译器生成取地址运算符函数。   
        
    经过我们的分析可以这样理解:对于一个没有实例化的空类,编译器是不会给它生成任何函数的,当实例化一个空类后,编译器会根据需要生成相应的函数。这条理论同样适合非空类(只声明变量,而不声明函数)。

    类的const成员变量、static const、static成员变量的初始化 

    结论:

    • 静态常量数据成员可以在类内初始化(即类内声明的同时初始化),也可以在类外,即类的实现文件中初始化,不能在构造函数中初始化,也不能在构造函数的初始化列表中初始化;
    • 静态非常量数据成员只能在类外,即类的实现文件中初始化,也不能在构造函数中初始化,不能在构造函数的初始化列表中初始化;
    • 非静态的常量数据成员不能在类内初始化,也不能在构造函数中初始化,而只能且必须在构造函数的初始化列表中初始化;
    • 非静态的非常量数据成员不能在类内初始化,可以在构造函数中初始化,也可以在构造函数的初始化列表中初始化;

    总结如下表:

    类型 初始化方式

    类内(声明)

    类外(类实现文件)

    构造函数中

    构造函数的初始化列表

    非静态非常量数据成员

    N

    N

    Y

    Y

    非静态常量数据成员

    N

    N

    N

    Y (must)

    静态非常量数据成员

    N

    Y (must)

    N

    N

    静态常量数据成员

    Y

    Y

    N

    N

    /**
    * <Effective C++>, page 14
    * const data of class
    * platform: visual studio 2005, win32
    * filename: item2.1.cpp
    */
    #include <iostream>
    using namespace std;
    &nbsp;
    class MyTest
    {
        //(1) error C2864: 'MyTest::MaxNumber1' : only static const integral data members can be initialized within a class
        //int MaxNumber1 = 5;
    &nbsp;
        //(2) error C2864: 'MyTest::MaxNumber2' : only static const integral data members can be initialized within a class
        //const int MaxNumber2 = 5;
    &nbsp;
        //(3) error C2864: 'MyTest::MaxNumber3' : only static const integral data members can be initialized within a class
        //static int MaxNumber3 = 5;
    &nbsp;
        //(4) ok
        static const int MaxNumber4 = 5;
        static const char cconst4 = 'B';
    &nbsp;
        //(5) error C2864: 'MyTest::dconst4' : only static const integral data members can be initialized within a class
        //static const double dconst4 = 200.00;
    &nbsp;
    public:
        //(6) error C2758: 'MyTest::MaxNumber2' : must be initialized in constructor base/member initializer list
        MyTest()
        {
            cout<<"MyTest constructor! "<<endl;
            cout<<"MaxNumber4 = "<<MaxNumber4<<endl;
            cout<<"cconst4 = "<<cconst4<<endl;
        }
    };
    &nbsp;
    int main()
    {
        MyTest obj;
    
        return 0;
    }

    代码注释中的(1),(2),(3)表示step编号。

    (1),(2),(3)中,我们可以看出,只有static const integral data member(静态整型常量数据成员)才能在类内初始化。从(4),(5)中也可以得到证明。其中,char型相当于整型。

    运行结果如下。

    MyTest constructor!

    MaxNumber = 5

    cconst1 = A

    cconst2 = B

    dconst1 = 100

    /**
    * <Effective C++>, page 14
    * const data of class
    * platform: visual studio 2005, win32
    * filename: item2.2.cpp
    */
    #include <iostream>
    using namespace std;
    &nbsp;
    class MyTest
    {
        int MaxNumber1;
        const int MaxNumber2;
        static int MaxNumber3;
    &nbsp;
        static const int MaxNumber4 = 5;
        static const char cconst4 = 'B';
    &nbsp;
        static const int MaxNumber5;
    &nbsp;
    public:
        //(1) error C2758: 'MyTest::MaxNumber2' : must be initialized in constructor base/member initializer list
        //(4) error C2438: 'MaxNumber3' : cannot initialize static class data via constructor
        //(7) error C2438: 'MaxNumber5' : cannot initialize static class data via constructor
        MyTest():MaxNumber1(5), MaxNumber2(5)//, MaxNumber5(5)//, MaxNumber3(5)
        {
            //(2) error C2166: l-value specifies const object
            //MaxNumber2 = 5;
    &nbsp;
            //(3) error LNK2001: unresolved external symbol "private: static int MyTest::MaxNumber3" (?MaxNumber3@MyTest@@0HA)
            //MaxNumber3 = 5;
    &nbsp;
            //(6) error C3892: 'MaxNumber5' : you cannot assign to a variable that is const
            //MaxNumber5 = 5;
    &nbsp;
            cout<<"MyTest constructor! "<<endl;
            cout<<"MaxNumber1 = "<<MaxNumber1<<endl;
            cout<<"MaxNumber2 = "<<MaxNumber2<<endl;
            cout<<"MaxNumber3 = "<<MaxNumber3<<endl;
            cout<<"MaxNumber4 = "<<MaxNumber4<<endl;
            cout<<"MaxNumber5 = "<<MaxNumber5<<endl;
            cout<<"cconst4 = "<<cconst4<<endl;
        }
    };
    &nbsp;
    //(5) ok
    int MyTest::MaxNumber3 = 5;
    &nbsp;
    //(8) ok
    const int MyTest::MaxNumber5 = 5;
    &nbsp;
    //(9) error C2761: 'int MyTest::MaxNumber1' : member function redeclaration not allowed
    //int MyTest::MaxNumber1 = 5;
    &nbsp;
    int main()
    {
        MyTest obj;
    &nbsp;
        return 0;
    }

    运行结果如下。

    MyTest constructor!

    MaxNumber1 = 5

    MaxNumber2 = 5

    MaxNumber3 = 5

    MaxNumber4 = 5

    MaxNumber5 = 5

    cconst4 = B

    代码注释中的(1),(2),(3)表示step编号。

    (1),(2)可以看出,非静态的常量数据成员必须在构造函数的初始化列表中初始化;如果在构造函数中初始化,会出现error c2166的错误,即常量对象是只读(read only)的,不能对其赋值。

    (3),(4),(5)可知,静态非常量数据成员只能在类外(类的实现文件)初始化。

    (6),(7),(8)可知,静态常量数据成员也可以在类外(类的实现文件)初始化。

    3 初始化顺序

    c++ 中类变量的初始化顺序,大体如下:

    1. 基类的静态成员初始化;
    2. 派生类的静态成员初始化;
    3. 基类的对象成员初始化;
    4. 基类的构造函数;
    5. 派生类的对象成员初始化;
    6. 派生类的构造函数;
    7. #include <iostream>
      using namespace std;
      
      class Inner{
      public:
          Inner(int i=0): in(i) { cout << "Inner.constructor. " << in<<"
      ";}
          Inner(Inner& inner): in(inner.in) { cout << "Inner.copy consturctor" << "
      ";};
          int getValue(){ return in; }
      
      private:
          int in;
      };
      
      class Inner2{
      public:
          Inner2(double i=0.0): in2(i) { cout << "Inner2.constructor. " << in2<<"
      ";}
          Inner2(Inner2& inner2): in2(inner2.in2) { cout << "Inner2.copy consturctor" << "
      ";};
          int getValue(){ return in2; }
      private:
          double in2;
      };
      
      class Base{
      public:
          Base(int v=0): value(v){ value = 1; cout << "Base.constructor. " << value<<"
      ";}
          Base(Base& b): value(b.value) { cout << "Base.copy consturctor" << "
      ";}
          int getValue(){ return value; }
      protected:
          int value;
          Inner2 inner2;
          static Inner inner;
          static int baseInt;
      };
      
      Inner Base::inner(1000);
      int Base::baseInt = 100;
      
      
      class Derive: public Base{
      public:
           Derive(string s = "hello", int base=0, int i=0, double i2=0): derive(s), in2(i2), in(i) { cout << "derive.derive == " << derive << ", Derive.consturctor" << "
      ";};
           //Derive(string s = "hello", int base=0, int i=0, double i2=0){ cout << "Derive.consturctor" << "
      ";};
          Derive(Derive& d): derive(d.derive) { cout << "Derive.copy consturctor" << "
      ";}
          void printValue(){
              cout << "derive.base.value == " << value << ", derive.derive == " << derive <<
                      ", derive.in == " << in.getValue() << ", derive.in2 == " << in2.getValue()
                      <<"base int: "<<baseInt<< "
      "; }
      
      private:
          string derive;
          Inner in;
          Inner2 in2;
          static Inner inner;
          //static int baseInt;
      };
      
      Inner Derive::inner(100);
      int main()
      {
          Derive d("world", 10, 20, 30.0);
          d.printValue();
          // Derive d2 = d;
          cout << endl;
      }

      Inner.constructor. 1000

      Inner.constructor. 100

      Inner2.constructor. 0

      Base.constructor. 1

      Inner.constructor. 20

      Inner2.constructor. 30

      derive.derive == world, Derive.consturctor

      derive.base.value == 1, derive.derive == world, derive.in == 20, derive.in2 == 30base int: 100

       

  • 相关阅读:
    springboot缓存注解——@CacheEvict
    springboot缓存注解——@CachePut
    springboot缓存注解——@Cacheable
    缓存环境
    JSR107缓存规范
    springboot自定义starter
    Linux 最常用命令整理,建议收藏!
    Spring Boot 2.2.0 正式发布,支持 JDK 13!
    厉害了,阿里史上最重要的三段代码…
    代码写成这样,老夫无可奈何!
  • 原文地址:https://www.cnblogs.com/yxzfscg/p/4769667.html
Copyright © 2020-2023  润新知