美文网首页
构造函数涉及到的一些问题

构造函数涉及到的一些问题

作者: tingjieee_19e5 | 来源:发表于2018-04-18 20:49 被阅读0次

    构造函数不同形式

    1. 默认的构造函数'
    2. 构造函数初始化列表;
    3. 拷贝构造函数;
    4. 复制运算符,如果类不允许复制,把拷贝构造函数和赋值运算符声明为private;
    5. 构造函数为explicit时,或者指定构造函数为explicit时;
    class A {
    public:
        A() {}//默认构造函数
        explicit A(int xx) { x = xx;}
        explicit A(int ii) : x(ii+1), y(ii) {}
        A(const A &a) { x = a.x;}
        A& operator = (const A &a){
            if(&a != this){
                A tmp(a);
                x = tmp.x;
              }
                return *this;
          }
    private:
         int x;
         const int y;
    };
    int main() {
        A a(1);
        A b = 1;
    //报错,"conversion  from 'int' to non-scalar type 'A' requested",
    //编译器无法找到找到int 类型转换为A类型的转换函数了,如果没加explicit,参数可以隐式的把int转换为A类型
    //报错,编译器无法找到拷贝构造函数,编译器构造出临时变量然后再调用拷贝构造函数,现在explicit修饰后只能显式的调用
        return 0;
    }
    

    Q. 为什么构造函数不能是虚函数?
    A. 1. 构造一个对象,必须知道对象的实际类型,而虚函数是动态的,在运行时确定实际类型的,而在构造对象时,由于对象还没有构造成功,编译器无法得知对象实际是类本身还是派生类,无法确定;
    2.虚函数的执行依赖虚函数表,虚函数表在构造函数中进行初始化工作,即初始化vptr,让它指向正确的虚函数表,而构造期间,虚函数表还没有被初始化,所以无法运行。
    Q. 复制构造函数,如果传入的参数是传值,形参复制到实参会调用构造函数,导致复制构造函数内调用复制构造函数,形成递归调用导致栈溢出。将复制构造函数传入参数改为常量引用。
    e.g.

    class A {
    private:
        int x;
    public:
        A(int i) { x = i;}
        A(A other) {x = other.x} //复制构造函数 error,
        A(const A &other) { x= other.x;}
        A& oprator= (const )
    };
    A a0(1);
    A a1 = a0;
    

    构造函数,构造函数初始化列表,explicit的构造函数

    1. 构造函数
      1.1 构造函数与类名相同,没有返回类型,有参数列表,有函数体,可以有多个构造函数
      1.2 构造函数不能被声明为const,创建类的 一个const对象时,直到构造函数完成初始化过程,对象才取得const属性。
      1.3 构造函数初始化列表,成员初始化顺序根据在类中定义的出现顺序一致
      1.4 =default 的含义,
    class A {
        A() = default;//不接受任何实参,所以是一个默认构造函数
    //默认构造函数是inline的,如果在
    };
    
    1. explicit 可以取消构造函数的隐式转换
    class CxString  // 没有使用explicit关键字的类声明, 即默认为隐式声明 
    { 
    public: 
        char *_pstr; 
        int _size; 
        CxString(int size)  { 
            _size = size;                // string的预设大小 
            _pstr = malloc(size + 1);    // 分配string的内存 
            memset(_pstr, 0, size + 1); 
        } 
        CxString(const char *p)  { 
            int size = strlen(p); 
            _pstr = malloc(size + 1);    // 分配string的内存 
            strcpy(_pstr, p);            // 复制字符串 
            _size = strlen(_pstr); 
        } 
    }; 
        // 析构函数这里不讨论, 省略... 
      CxString string1(24);    // 这样是OK的, 为CxString预分配24字节的大小的内存 
      CxString string2 = 10;    // 这样是OK的, 为CxString预分配10字节的大小的内存 
      CxString string3;        // 这样是不行的, 因为没有默认构造函数, 错误为: “CxString”: 没有合适的默认构造函数可用 
    

    "CxString string2 = 10;" 这句为什么是可以的呢? 在C++中, 如果的构造函数只有一个参数时, 那么在编译的时候就会有 一个缺省的转换操作:将该构造函数对应数据类型的数据转换为该类对象. 也就是说 "CxString string2 = 10;" 这段代码, 编译器自动将整型转换为CxString类对象, 实际上等同于下面的操作:

    CxString string2(10); 
    或者:
    CxString temp(10); 
    CxString string2 = temp; 
    

    这种隐式转换有时候会造成疑惑和隐患,所以为了避免这种隐式转换,可以使用explicit关键字来取消隐式转换:

    
    class CxString  // 没有使用explicit关键字的类声明, 即默认为隐式声明 
    { 
    public: 
        char *_pstr; 
        int _size; 
        CxString(int size)  { 
            _size = size;                // string的预设大小 
            _pstr = malloc(size + 1);    // 分配string的内存 
            memset(_pstr, 0, size + 1); 
        } 
        CxString(const char *p)  { 
            int size = strlen(p); 
            _pstr = malloc(size + 1);    // 分配string的内存 
            strcpy(_pstr, p);            // 复制字符串 
            _size = strlen(_pstr); 
        } 
    }; 
        // 析构函数这里不讨论, 省略... 
      CxString string1(24);    // 这样是OK的, 为CxString预分配24字节的大小的内存 
      CxString string2 = 10;    // 这样是wrong的,因为explicit关键字取消了隐式转换
      CxString string3;        // 这样是不行的, 因为没有默认构造函数, 错误为: “CxString”: 没有合适的默认构造函数可用 
    

    因为隐式转换只针对构造函数只有一个参数,如果构造函数有多个,隐式转换是无效的

    高质量的赋值运算符函数

    class CMyString {
    public:
        CMyString(char *pData = NULL);
        CMyString(const CMyString& str);
        ~CMyString(void);
    private:
        char *m_pData;
    };
    
    CMyString& CMyString::operator = (const CmyString &str){
        if(this != &str){
          CMyString strTemp(str);
    
          char *pTemp = strTemp.m_pData;
          strTemp.m_pData = m_pData;
          m_pData = pTemp; //把局部变量的变量内存地址给类
        }// 离开作用域,即自动释放局部对象的内存。
        return *this;
    } 
    
    1. 构造函数

    Q. 为什么构造函数不能是虚函数?

    A. 1. 构造一个对象,必须知道对象的实际类型,而虚函数是动态的,在运行时确定实际类型的,而在构造对象时,由于对象还没有构造成功,编译器无法得知对象实际是类本身还是派生类,无法确定;

    2.虚函数的执行依赖虚函数表,虚函数表在构造函数中进行初始化工作,即初始化vptr,让它指向正确的虚函数表,而构造期间,虚函数表还没有被初始化,所以无法运行。

    Q. 复制构造函数,如果传入的参数是传值,形参复制到实参会调用构造函数,导致复制构造函数内调用复制构造函数,形成递归调用导致栈溢出。将复制构造函数传入参数改为常量引用。

    e.g.

    
    class A {
    
    private:
    
        int x;
    
    public:
    
        A(int i) { x = i;}
    
        A(A other) {x = other.x} //复制构造函数 error,
    
        A(const A &other) { x= other.x;}
    
        A& oprator= (const )
    
    };
    
    A a0(1);
    
    A a1 = a0;
    
    

    构造函数,构造函数初始化列表,explicit的构造函数

    1. 构造函数

    1.1 构造函数与类名相同,没有返回类型,有参数列表,有函数体,可以有多个构造函数

    1.2 构造函数不能被声明为const,创建类的 一个const对象时,直到构造函数完成初始化过程,对象才取得const属性。

    1.3 构造函数初始化列表,成员初始化顺序根据在类中定义的出现顺序一致

    1.4 =default 的含义,

    
    class A {
    
        A() = default;//不接受任何实参,所以是一个默认构造函数
    
    //默认构造函数是inline的,如果在
    
    };
    
    
    1. explicit 可以取消构造函数的隐式转换
    
    class CxString  // 没有使用explicit关键字的类声明, 即默认为隐式声明 
    
    { 
    
    public: 
    
        char *_pstr; 
    
        int _size; 
    
        CxString(int size)  { 
    
            _size = size;                // string的预设大小 
    
            _pstr = malloc(size + 1);    // 分配string的内存 
    
            memset(_pstr, 0, size + 1); 
    
        } 
    
        CxString(const char *p)  { 
    
            int size = strlen(p); 
    
            _pstr = malloc(size + 1);    // 分配string的内存 
    
            strcpy(_pstr, p);            // 复制字符串 
    
            _size = strlen(_pstr); 
    
        } 
    
    }; 
    
        // 析构函数这里不讨论, 省略... 
    
      CxString string1(24);    // 这样是OK的, 为CxString预分配24字节的大小的内存 
    
      CxString string2 = 10;    // 这样是OK的, 为CxString预分配10字节的大小的内存 
    
      CxString string3;        // 这样是不行的, 因为没有默认构造函数, 错误为: “CxString”: 没有合适的默认构造函数可用 
    
    

    "CxString string2 = 10;" 这句为什么是可以的呢? 在C++中, 如果的构造函数只有一个参数时, 那么在编译的时候就会有 一个缺省的转换操作:将该构造函数对应数据类型的数据转换为该类对象. 也就是说 "CxString string2 = 10;" 这段代码, 编译器自动将整型转换为CxString类对象, 实际上等同于下面的操作:

    
    CxString string2(10); 
    
    或者:
    
    CxString temp(10); 
    
    CxString string2 = temp; 
    
    

    这种隐式转换有时候会造成疑惑和隐患,所以为了避免这种隐式转换,可以使用explicit关键字来取消隐式转换:

    
    class CxString  // 没有使用explicit关键字的类声明, 即默认为隐式声明 
    
    { 
    
    public: 
    
        char *_pstr; 
    
        int _size; 
    
        CxString(int size)  { 
    
            _size = size;                // string的预设大小 
    
            _pstr = malloc(size + 1);    // 分配string的内存 
    
            memset(_pstr, 0, size + 1); 
    
        } 
    
        CxString(const char *p)  { 
    
            int size = strlen(p); 
    
            _pstr = malloc(size + 1);    // 分配string的内存 
    
            strcpy(_pstr, p);            // 复制字符串 
    
            _size = strlen(_pstr); 
    
        } 
    
    }; 
    
        // 析构函数这里不讨论, 省略... 
    
      CxString string1(24);    // 这样是OK的, 为CxString预分配24字节的大小的内存 
    
      CxString string2 = 10;    // 这样是wrong的,因为explicit关键字取消了隐式转换
    
      CxString string3;        // 这样是不行的, 因为没有默认构造函数, 错误为: “CxString”: 没有合适的默认构造函数可用 
    
    

    因为隐式转换只针对构造函数只有一个参数,如果构造函数有多个,隐式转换是无效的

    构造函数的构造顺序

    1. 静态成员的构造函数
    2. 虚拟继承类的构造函数
    3. 非虚拟继承类的构造函数
    4. 成员构造函数 ,按照声明顺序
    5. 类自己的构造函数

    相关文章

      网友评论

          本文标题:构造函数涉及到的一些问题

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