C++类中的4个特殊函数 - 缺省构造函数、拷贝构造函数、拷贝赋值操作符和析构函数

a. C++标准中提到“The default constructor, copy constructor and copy assignment operator, and destructor are special member functions.[Note: The implementation will implicitly declare these member functions for some class types when the program does not explicitly declare them. The implementation will implicitly define them if they are used.]”。即缺省构造函数、拷贝构造函数、拷贝赋值操作符和析构函数是特殊成员函数。


b. “Constructors do not have names. A special declarator syntax using an optional sequence of function- specifiers(inline, virtual and explicit) followed by the constructor’s class name followed by a parameter list is used to declare or define the constructor.” 构造函数没有名称。


c. 构造函数不能有返回类型,也不能由virtual, const, static 和 volatile来修饰。但可以由inline来修饰,事实上隐式构造函数就是用inline来修饰的。inline表示编译时展开,通常速度块;virtual表示运行时绑定,通常意味着灵活。


d. 类中存在虚函数或者有虚基类的情况下需要显式声明构造函数。拷贝构造函数也是如此。


f. 构造函数是一种特殊函数,而拷贝构造函数是一种特殊的构造函数。类X的构造函数的第一个参数必须为X&,或者const X&;除了第一个参数外,构造函数要么不存在其他参数,如果存在其他参数,其他参数必须有默认值。一个类可以有多个拷贝构造函数。它的形式如下:

X::X(X& x)

X::X(const X& x)

X::X(X& x, int a = 0, int b = 1…)


g. 什么时候会调用拷贝构造函数?

以下三种情况出现时,会调用一个类的拷贝构造函数:

1) 用一个已经实例化了的该类对象,去实例化该类的另外一个对象;

2) 用该类的对象传值的方式作为一个函数的参数;

3) 一个函数返回值为该类的一个对象。


运行下面代码以验证之:

#include

using namespace std;


class CA

{

public:

int a;

int b;

public:

inline CA()

{

a = 1;

b = 1;

}


inline CA(int A, int B)

{

a = A;

b = B;

}


inline CA(CA& x)

{

a = x.a;

b = x.b;

cout << "copy constructor is called." << endl;

}


void printInfo()

{

cout << "a = " << a << ", b = " << b << endl;

}

};


int someFun1(CA x)

{

return x.a + x.b;

}


CA someFun2(int a, int b)

{

CA ca(a, b);

return ca;

}


int main(void)

{

CA a;

// CA b(); // 不能用这种

方式声明CA的对象b!

CA c(10, 10);


CA d(c); // 情况1) -> 调用拷贝构造函数

int anInt = someFun1(c); // 情况2) -> 调用拷贝构造函数

CA e = someFun2(11, 11); // 情况3) -> 调用拷贝构造函数


return 0;

}

运行结果:



运行结果表明,上述结论是正确的。


h. 什么时候必须要显式声明拷贝构造函数?

拷贝构造函数的作用就是用一个已经实例化了的该类对象,去实例化该类的另外一个对象。


1) 下面的代码并没有显式声明一个构造函数,编译器会自动为类CExample1生成一个缺省的隐式拷贝构造函数:

#include

using namespace std;


class CExample1

{

private:

int a;


public:

CExample1(int b){a = b;}

void SetValue(int a){this->a = a;}

void Show(){cout << a << endl;}

};


int main(void)

{

CExample1 A(100);

CExample1 B = A; // 调用了缺省的隐式拷贝构造函数

CExample1 C(B); // 调用了缺省的隐式拷贝构造函数


B.Show(); // 输出应该是100

B.SetValue(90);

B.Show(); // 输出应该是90

A.Show(); // 输出应该是100

C.Show(); // 输出应该是100


return 0;

}

输出为:



2) 如果有成员变量以指针形式存在,涉及动态内存分配等情况下,一定要显式声明拷贝构造函数。要注意到,如果需要显式定义拷贝构造函数,那么通常都是需要同时定义析构函数(因为通常涉及了动态内存分配),至于是否必须重载操作符“=”,要视情况而定。


#include

using namespace std;


class CSomething

{

public:

int a;

int b;


public:

CSomething(int a, int b)

{this->a = a; this->b = b;}

};


class CA

{

private:

CSomething* sth; // 以指针形式存在的成员变量


public:

CA(CSomething* sth){this->sth = new CSomething(sth->a, sth->b);}

~CA()

{

cout << "In the destructor of class CA..." << endl;

if (NULL != sth) delete sth;

}

void Show(){cout << "(" << sth->a << ", " << sth->b << ")" << endl;}

void setValue(int a, int b){sth->a = a; sth->b = b;}

void getSthAddress()

{

cout << sth << endl;

}

};


int main(void)

{

CSomething sth(1, 2);

CA ca(&sth);

ca.Show();


CA cb(ca); // 调用缺省的隐式拷贝构造函数

cb.Show(

);


cb.setValue(2, 3);

ca.Show();

cb.Show();


ca.getSthAddress();

cb.getSthAddress();


return 0;

}

上面的程序没有显式声明拷贝构造函数,运行结果如下:



可见,ca和cb中的指针成员变量sth指向的是同一个内存地址(Console输出的第5、6行),这就是为什么在cb.setValue(2, 3)后,ca对应的内容也发生了改变(Console输出的第3、4行),而这不是我们所期望的;其次,我们生成了两个对象ca和cb,因此对两次调用析构函数,第一次调用析构函数的时候没有问题,因为此时sth里面有内容,第二次调用析构函数时,sth里面的内容由于在第一次调用析构函数的时候已经被delete了,所以会出现如上的错误提示。


保持其他代码不变,现在我们增加一个拷贝构造函数如下:

CA(CA& obj)

{

sth = new CSomething((obj.sth)->a, (obj.sth)->b);

}

再运行上面的程序,所得到的结果如下:



这次,ca和cb中的指针成员变量sth指向的不是同一个内存地址(Console输出的第5、6行)了,这就是为什么在cb.setValue(2, 3)后,ca对应的内容保持不变,而cb的内容该如愿地改为(2, 3)(Console输出的第3、4行);其次,析构函数也不会报告错误了。


3) 关于拷贝构造函数另外一个完整的例子,其中包含了copy constructor,destructor 和copy assignment operator。

#include

using namespace std;


class Point

{

public:

int _x;

int _y;


public:

Point();

Point(int, int);

};


Point::Point()

{

_x = 0;

_y = 0;

}


Point::Point(int x, int y)

{

_x = x;

_y = y;

}


class CA

{

public:

Point* _point;


public:

CA(const Point*);

void setPointValues(int, int);

void printCoordinates();


// 需要增加的拷贝构造函数

CA(const CA&);

// 需要增加的析构函数

virtual ~CA();

// 需要增加的拷贝赋值函数

CA& operator = (const CA&);

};


CA::CA(const Point* point)

{

_point = new Point(); // 发生了动态内存分配!因此不能缺少析构函数。

_point->_x = point->_x;

_point->_y = point->_y;

}


// 需要增加的拷贝构造函数的实现

CA::CA(const CA& ca)

{

_point = new Point();

_point->_x = (ca._point)->_x;

_point->_y = (ca._point)->_y;

}


// 需要增加的析构函数的实现

CA::~CA()

{

if(!_point) delete _point;

}


// 需要增加的拷贝赋值函数的实现

CA& CA::operator = (const CA& ca)

{

_point = new Point();

_point->_x = (ca._point)->_x;


_point->_y = (ca._point)->_y;


return *this;

}


void CA::setPointValues(int x, int y)

{

_point->_x = x;

_point->_y = y;

}


void CA::printCoordinates()

{

cout << "Coordinates = (" << _point->_x << ", " << _point->_y << ")" << endl;

}


int main(void)

{

Point apoint(1, 2);

CA ca(&apoint);

ca.printCoordinates();


CA cb(ca); // 调用拷贝构造函数

cb.printCoordinates();


cb.setPointValues(12, 12);

cb.printCoordinates();

ca.printCoordinates();


CA cc = cb; // 调用拷贝赋值函数

cc.printCoordinates();

cc.setPointValues(13, 13);



ca.printCoordinates();

cb.printCoordinates();

cc.printCoordinates();


return 0;

}

相关文档
最新文档