阐明一下,我用的是g++7.1.0编译器,规范库源代码也是这个版本的。

本篇文章解说c++11中,类的构造函数品种,以及不显式申明的状况下是否会主动生成。

1. 类的构造函数类别

在我刚接触c++的时候,我始终晓得类能够有四种模式的构造函数,即无参构造函数、有参构造函数、拷贝构造函数、赋值运算符构造函数,最近看规范IO源代码,发现又多了一种,那就是挪动构造函数,这是c++11中补充进来的,所以当初c++能够领有四种模式的构造函数,即无参构造函数、有参构造函数、拷贝构造函数、赋值构造函数、挪动构造函数、挪动赋值构造函数。

阐明以下:赋值运算符operator=到底算不算构造函数,这个集体有集体的认识,不多探讨,然而单就阐明构造函数的时候把它漏掉的话,我感觉有点耍流氓了,所以也要把它列进来。

一个残缺的申明了这六种构造函数以及应用的案例如下:

#include <iostream>#include <string.h>using namespace std;class CPtr{    private:        char *m_pData;        int m_iSize;    public:        //without param constructors        CPtr()        {            m_iSize = 1024;            m_pData = new char[m_iSize];        }        ~CPtr()        {            if ( m_pData != nullptr )            {                delete []m_pData;                m_pData = nullptr;            }        }        //with param constructors        CPtr(const int p_iSize)        {            m_iSize = p_iSize;            m_pData = new char[p_iSize];        }        //copy constructors        CPtr(const CPtr& ptr)        {            if (ptr.m_pData != nullptr)            {                m_iSize = strlen(ptr.m_pData)+1;                m_pData = new char[m_iSize];                strncpy(m_pData, ptr.m_pData, m_iSize-1);            }        }        //move constructors        CPtr(CPtr&& ptr)        {            m_pData = ptr.m_pData;            m_iSize = ptr.m_iSize;            ptr.m_pData = nullptr;            ptr.m_iSize = 0;        }        //赋值构造函数        CPtr& operator=(const CPtr& ptr)        {            if (ptr.m_pData != nullptr)            {                m_iSize = strlen(ptr.m_pData)+1;                m_pData = new char[m_iSize];                strncpy(m_pData, ptr.m_pData, m_iSize-1);            }            return *this;        }            //挪动赋值构造函数        CPtr& operator=(CPtr&& ptr)        {            m_pData = ptr.m_pData;            m_iSize = ptr.m_iSize;            ptr.m_pData = nullptr;            ptr.m_iSize = 0;            return *this;        }        void setData(const char* str)        {            if (  str == nullptr)            {                cout << "str is nullptr" << endl;                return;            }            if ( m_iSize == 0)            {                cout << "the memory is nothing" << endl;                return;            }            int iSize = strlen(str);            if ( iSize < m_iSize )            {                strncpy(m_pData, str, iSize);            }            else            {                strncpy(m_pData, str, m_iSize-1);            }        }        void print(const char* object)        {            cout << object << "'s data is " << m_pData << endl;        }};int main(){    CPtr p1(1024);    p1.setData("lilei and hanmeimei");    p1.print("p1");    CPtr p2(p1);    p2.print("p2");    CPtr p3 = p1;    p3.print("p3");    CPtr p4(move(p1));    p4.print("p4");    CPtr p5 = move(p2);    p5.print("p5");    return 0;}
这里move是规范库的一个函数,返回一个右值援用,也就是CPtr &&类型。

这里咱们是显示申明了所有的构造函数,接下来看看编译器对于class构造函数的隐式生成规定。

2. 构造函数默认生成规定

2.1 没有显式申明任何构造函数

编译器会主动生成默认的无参构造函数,这一点咱们是能够必定的,那另外几种构造函数也会默认生成吗,这个就不太确定了。

咱们把下面那段代码批改一下,如下:

#include <iostream>#include <string.h>using namespace std;class CPtr{    private:        char *m_pData;        int m_iSize;    public:        //without param constructors        /*        CPtr()        {            m_iSize = 1024;            m_pData = new char[m_iSize];        }        ~CPtr()        {            if ( m_pData != nullptr )            {                delete []m_pData;                m_pData = nullptr;            }        }        //with param constructors        CPtr(const int p_iSize)        {            m_iSize = p_iSize;            m_pData = new char[p_iSize];        }        //copy constructors        CPtr(const CPtr& ptr)        {            if (ptr.m_pData != nullptr)            {                m_iSize = strlen(ptr.m_pData)+1;                m_pData = new char[m_iSize];                strncpy(m_pData, ptr.m_pData, m_iSize-1);            }        }        //move constructors        CPtr(CPtr&& ptr)        {            m_pData = ptr.m_pData;            m_iSize = ptr.m_iSize;            ptr.m_pData = nullptr;            ptr.m_iSize = 0;        }        //赋值构造函数        CPtr& operator=(const CPtr& ptr)        {            if (ptr.m_pData != nullptr)            {                m_iSize = strlen(ptr.m_pData)+1;                m_pData = new char[m_iSize];                strncpy(m_pData, ptr.m_pData, m_iSize-1);            }            return *this;        }            //挪动赋值构造函数        CPtr& operator=(CPtr&& ptr)        {            m_pData = ptr.m_pData;            m_iSize = ptr.m_iSize;            ptr.m_pData = nullptr;            ptr.m_iSize = 0;            return *this;        }        */        void setData(const char* str)        {            if (  str == nullptr)            {                cout << "str is nullptr" << endl;                return;            }            if ( m_iSize == 0)            {                cout << "the memory is nothing" << endl;                return;            }            int iSize = strlen(str);            if ( iSize < m_iSize )            {                strncpy(m_pData, str, iSize);            }            else            {                strncpy(m_pData, str, m_iSize-1);            }        }        void print(const char* object)        {            cout << object << "'s data is " << m_pData << endl;        }};int main(){    CPtr p1;    //p1.setData("lilei and hanmeimei");    //p1.print("p1");    CPtr p2(p1);    //p2.print("p2");    CPtr p3 = p1;    //p3.print("p3");    CPtr p4(move(p1));    //p4.print("p4");    CPtr p5 = move(p2);    //p5.print("p5");    CPtr p6(1024);    return 0;}

把所有的构造函数都正文掉,而后对上述代码进行编译,报错,报错信息如下:

test.cpp: 在函数‘int main()’中:test.cpp:110:14: 谬误:no matching function for call to ‘CPtr::CPtr(int)’  CPtr p6(1024);              ^test.cpp:5:7: 附注:candidate: CPtr::CPtr() class CPtr       ^~~~test.cpp:5:7: 附注: 备选须要 0 实参,但提供了 1 个test.cpp:5:7: 附注:candidate: constexpr CPtr::CPtr(const CPtr&)test.cpp:5:7: 附注:  no known conversion for argument 1 from ‘int’ to ‘const CPtr&’test.cpp:5:7: 附注:candidate: constexpr CPtr::CPtr(CPtr&&)test.cpp:5:7: 附注:  no known conversion for argument 1 from ‘int’ to ‘CPtr&&’

从错误信息咱们能够看到两点,一是带int类型参数的构造函数是不会主动生成的,二是类CPtr是存在拷贝结构和挪动结构的,接着咱们当初把p6那一行正文掉,再编译,就通过了,也就是说对于class类型,当没有显式申明任何构造函数的时候,编译器除了默认生成无参构造函数以外,还会主动生成拷贝构造函数、赋值构造函数、挪动构造函数、挪动赋值构造函数,并且主动生成的构造函数都是public的,因为它们是能够用于生成对象的,而对于有参构造函数,因为参数是未知的,所以编译器没有方法主动生成。

也就是说,当没有显式申明任何构造函数时,会默认生成五种构造函数,即:一般构造函数、拷贝构造函数、赋值构造函数、挪动构造函数、挪动赋值构造函数,而对于有参结构,除非显式指定,否则工作状况下不会主动生成。

2.2 显式申明一般构造函数

还是之前的代码,再批改一下:

#include <iostream>#include <string.h>using namespace std;class CPtr{    private:        char *m_pData;        int m_iSize;    public:        //without param constructors        CPtr()        {            m_iSize = 1024;            m_pData = new char[m_iSize];        }        ~CPtr()        {            if ( m_pData != nullptr )            {                delete []m_pData;                m_pData = nullptr;            }        }        /*        //with param constructors        CPtr(const int p_iSize)        {            m_iSize = p_iSize;            m_pData = new char[p_iSize];        }        //copy constructors        CPtr(const CPtr& ptr)        {            if (ptr.m_pData != nullptr)            {                m_iSize = strlen(ptr.m_pData)+1;                m_pData = new char[m_iSize];                strncpy(m_pData, ptr.m_pData, m_iSize-1);            }        }        //move constructors        CPtr(CPtr&& ptr)        {            m_pData = ptr.m_pData;            m_iSize = ptr.m_iSize;            ptr.m_pData = nullptr;            ptr.m_iSize = 0;        }        //赋值构造函数        CPtr& operator=(const CPtr& ptr)        {            if (ptr.m_pData != nullptr)            {                m_iSize = strlen(ptr.m_pData)+1;                m_pData = new char[m_iSize];                strncpy(m_pData, ptr.m_pData, m_iSize-1);            }            return *this;        }            //挪动赋值构造函数        CPtr& operator=(CPtr&& ptr)        {            m_pData = ptr.m_pData;            m_iSize = ptr.m_iSize;            ptr.m_pData = nullptr;            ptr.m_iSize = 0;            return *this;        }        */        void setData(const char* str)        {            if (  str == nullptr)            {                cout << "str is nullptr" << endl;                return;            }            if ( m_iSize == 0)            {                cout << "the memory is nothing" << endl;                return;            }            int iSize = strlen(str);            if ( iSize < m_iSize )            {                strncpy(m_pData, str, iSize);            }            else            {                strncpy(m_pData, str, m_iSize-1);            }        }        void print(const char* object)        {            cout << object << "'s data is " << m_pData << endl;        }};int main(){    CPtr p1;    //p1.setData("lilei and hanmeimei");    //p1.print("p1");    CPtr p2(p1);    //p2.print("p2");    CPtr p3 = p1;    //p3.print("p3");    CPtr p4(move(p1));    //p4.print("p4");    CPtr p5 = move(p2);    //p5.print("p5");    return 0;}

下面的代码编译通过,阐明当只显示申明了一般构造函数的时候,会主动生成拷贝构造函数、赋值构造函数、挪动构造函数、挪动赋值构造函数这四种。

2.3 显式申明拷贝构造函数

首先看只显式申明一个拷贝构造函数的状况,如下:

#include <iostream>#include <string.h>using namespace std;class CPtr{    private:        char *m_pData;        int m_iSize;    public:        //without param constructors        /*        CPtr()        {            m_iSize = 1024;            m_pData = new char[m_iSize];        }        */        ~CPtr()        {            if ( m_pData != nullptr )            {                delete []m_pData;                m_pData = nullptr;            }        }        /*        //with param constructors        CPtr(const int p_iSize)        {            m_iSize = p_iSize;            m_pData = new char[p_iSize];        }        */        //copy constructors        CPtr(const CPtr& ptr)        {            if (ptr.m_pData != nullptr)            {                m_iSize = strlen(ptr.m_pData)+1;                m_pData = new char[m_iSize];                strncpy(m_pData, ptr.m_pData, m_iSize-1);            }        }        //move constructors        /*        CPtr(CPtr&& ptr)        {            m_pData = ptr.m_pData;            m_iSize = ptr.m_iSize;            ptr.m_pData = nullptr;            ptr.m_iSize = 0;        }        */        //赋值构造函数        /*        CPtr& operator=(const CPtr& ptr)        {            if (ptr.m_pData != nullptr)            {                m_iSize = strlen(ptr.m_pData)+1;                m_pData = new char[m_iSize];                strncpy(m_pData, ptr.m_pData, m_iSize-1);            }            return *this;        }            */        //挪动赋值构造函数        /*        CPtr& operator=(CPtr&& ptr)        {            m_pData = ptr.m_pData;            m_iSize = ptr.m_iSize;            ptr.m_pData = nullptr;            ptr.m_iSize = 0;            return *this;        }        */        void setData(const char* str)        {            if (  str == nullptr)            {                cout << "str is nullptr" << endl;                return;            }            if ( m_iSize == 0)            {                cout << "the memory is nothing" << endl;                return;            }            int iSize = strlen(str);            if ( iSize < m_iSize )            {                strncpy(m_pData, str, iSize);            }            else            {                strncpy(m_pData, str, m_iSize-1);            }        }        void print(const char* object)        {            cout << object << "'s data is " << m_pData << endl;        }};int main(){    CPtr p1;    //p1.setData("lilei and hanmeimei");    //p1.print("p1");    CPtr p2(p1);    //p2.print("p2");    CPtr p3 = p1;    //p3.print("p3");    CPtr p4(move(p1));    //p4.print("p4");    CPtr p5 = move(p2);    //p5.print("p5");    return 0;}

编译报错如下:

test.cpp: 在函数‘int main()’中:test.cpp:107:7: 谬误:no matching function for call to ‘CPtr::CPtr()’  CPtr p1;       ^~test.cpp:36:3: 附注:candidate: CPtr::CPtr(const CPtr&)   CPtr(const CPtr& ptr)   ^~~~test.cpp:36:3: 附注: 备选须要 1 实参,但提供了 0 个

阐明当只申明拷贝构造函数时,连默认结构都不复存在,就没有方法申明第一个对象,这样必定是不行的,接下来勾销对于默认构造函数的正文,编译就通过了,接下来再勾销对于赋值构造函数的正文,编译还是能够通过。

也就是说当只申明拷贝构造函数的时候,其余结构包含一般结构都不会主动生成,而当申明了一般结构和拷贝结构时,挪动结构会主动生成。

3. 构造函数主动生成总结

总结一下,构造函数主动生成的规定:

  • 没有显式申明任何构造函数时,会主动生成一般构造函数、拷贝构造函数、赋值构造函数、挪动构造函数、挪动赋值构造函数五种;
  • 对于带一般参数的构造函数,任何状况下都不会主动生成;
  • 显式申明一般构造函数时,会主动生成拷贝构造函数、赋值构造函数、挪动构造函数、挪动赋值构造函数四种;
  • 只显式申明拷贝构造函数时,一般构造函数都不会主动生成,没有方法生成对象;
  • 显示申明一般构造函数和拷贝构造函数时,会主动生成挪动构造函数;

这些构造函数不要求总是全副显式申明,但咱们在应用class的时候最好显式申明这五种构造函数,避免出现一些不必要的问题。