对于C++初学者甚至是略有编程经验的研发人员来说,构造函数、拷贝构造函数、赋值操作符(赋值构造函数)既简单又难理解,甚至经常在这上面吃亏。网络上关于这三者的介绍也很多,其中不乏经典之作;但正是由于有关文章很多,良莠不齐,许多人的解释并不贴切,甚至是错误的,这导致许多初学者越看越糊涂。
在很多文章中,将这三者放在一起讨论,甚至某些权威出版社的C++书籍也是如此,将赋值操作符叫做“赋值拷贝函数”,这是很不贴切的。赋值操作符(==)和构造函数根本没有半点关系。构造函数和拷贝构造函数,都是在有新的对象产生的时候编译器内部自动调用的,而赋值操作符,只是将一个已有对象的值赋值给另一个已有对象,并不会产生新的对象,只是在行为上,有些许像拷贝构造函数。所以,赋值操作符说是赋值拷贝函数,是非常不准确的一种说法!记住,赋值操作符只是一种操作符重载而已,和构造函数并有半点关系!
构造函数其实很简单,就是创建具体对象的时候,编译器自动调用的类的特性函数,每个C++的类都会有。构造函数可以我们自己编写,如果我们没有自己编写,编译器会自动帮我们添加一个默认的构造函数。默认的构造函数不包含任何参数,对类里面的成员变量,也不会进行初始化。除了构造函数外,还有一个拷贝构造函数,如果我们没有手动编写编译器也会给我们自动增加一个。拷贝构造函数与构造函数的不同,是其包含一个类本身的const引用作为参数。例如编写了一个CTest类,其默认构造函数为CTest(),拷贝构造函数为CTest(const CTest& rTest)。需要注意的是,不管是构造函数还是拷贝构造函数,其成员变量的初始化,不应该放到函数体内,而是应该用成员初始化列表实现。(关于成员初始化列表,可自行查找,并不是本文的重点;另外,构造函数除了默认构造函数外,还可以带有各种参数,但是参数肯定不是其const引用,如果参数为其const引用,就是拷贝构造函数了;有关含有参数的构造函数,也需您自行查找资料)
既然说构造函数和拷贝构造函数是编译器自动调用的,那么编译器什么时候会调用构造函数,什么时候会调用拷贝构造函数呢?我们只需要记住拷贝构造函数被调用的三种情况即可,除了这三种情况外,其他的任何有新对象创建的情形都是调用构造函数。拷贝构造函数调用的三种情况分别是:(1)当函数的形参是类的对象时(也就是值传递时);(2)当函数的返回值是类的对象;(3)用类的一个对象去初始化另一个对象时。具体的含义也需您自行查找(编程最主要的学习渠道就是自己去找资料弄清楚)。理解了这三点,就不难明白为什么拷贝构造函数里面的参数是引用了,因为如果不是引用而是类的对象,那么拷贝构造函数的形参还会调用拷贝构造函数,一直调用下去,就无限循环了。
后面的代码是有关拷贝构造函数和构造函数的调用实例。
// "Constructor.h"
namespace ClassContructor
{
class CBaseClass
{
public:
CBaseClass();
CBaseClass(const CBaseClass& cOther);
CBaseClass& operator = (const CBaseClass& cOther);
virtual ~CBaseClass();
public: //此处为了测试方便,将成员变量声明为public,实际项目中,应该声明为private或者protected
int m_nBaseTest; //用作测试的数
static int m_nConstructorCount; //构造函数调用次数
static int m_nCopyCount; //拷贝构造函数调用次数
static int m_nAssignCount; //赋值操作符调用次数
};
void TestClassConstructor();
}
//Constructor.cpp
#include <iostream>
#include <list>
namespace ClassContructor
{
int CBaseClass::m_nConstructorCount = 0;
int CBaseClass::m_nCopyCount = 0;
int CBaseClass::m_nAssignCount = 0;
CBaseClass::CBaseClass():m_nBaseTest(10) //构造函数设置m_nBaseTest为10
{
++m_nConstructorCount; //构造函数次数+1
}
CBaseClass::CBaseClass(const CBaseClass& cOther):m_nBaseTest(cOther.m_nBaseTest + 1) //拷贝构造函数将m_nBaseTest值加1
{
++m_nCopyCount; //拷贝构造函数次数+1
}
CBaseClass& CBaseClass::operator = (const CBaseClass& cOther)
{
m_nBaseTest = cOther.m_nBaseTest;
++m_nAssignCount; //赋值操作符次数+1
return *this;
}
CBaseClass::~CBaseClass()
{
}
void TestClassConstructor()
{
std::list<CBaseClass> lstDerived;
for(int i = 0 ; i < 10; ++i)
{
/*先调用构造函数创建一个临时对象作为参数,push_back调用拷贝构造函数创建一个对象放到内存中,
所以,lstDerived中对象的m_nBaseTest应该是11而不是10
*/
lstDerived.push_back(CBaseClass());
}
std::cout << "constructor次数----------" << CBaseClass::m_nConstructorCount << std::endl;
std::cout << "copy次数----------" << CBaseClass::m_nCopyCount << std::endl;
std::cout << "=次数----------" << CBaseClass::m_nAssignCount << std::endl;
std::cout << "m_nBaseTest = " << lstDerived.back().m_nBaseTest << std::endl;
}
}
//main函数中调用
ClassContructor::TestClassConstructor();
main函数中调用后运行,结果如下: