C++空类的默认成员函数总结
副标题#e#
class Empty
{
public:
Empty(); //缺省结构函数
Empty(const Empty&); //拷贝结构函数
~Empty(); //析构函数
Empty& operator=(const Empty&); //赋值运算符
Empty* operator&(); //取值运算符
const Empty* operator&() const; // 取值运算符
};
譬喻有以下class:
class StringBad
{
private :
char * str;
int len;
public :
StringBad( const char * s);
StringBad();
~ StringBad();
} ;
在结构函数和析构函数界说傍边有如下界说:
StringBad::StringBad( const char * s)
{
len = std::strlen(s);
str = new char [len + 1 ];
}
StringBad::StringBad()
{
len = 4 ;
str = new char [ 4 ];
}
StringBad:: ~ StringBad()
{
delete [] str;
}
那么在措施傍边假如有以下代码:
StringBad sports( " Spinach Leaves Bow1 for bollars " );
StringBad sailor = sports;
以上的第二条初始化语句将会挪用什么结构函数?记着,这种形式的初始化等效于下面的语句:
StringBad sailor = StringBad(sports);
因为sports的范例为StringBad,因此相应的结构函数原型应该如下:
StringBad( const StringBad & );
当我们利用一个工具来初始化另一个工具时,编译器将自动生成上述结构函数(称为复制结构函数,因为它建设工具的一个副本)。
此刻我们来看看我们没有界说复制结构函数的环境下挪用隐式复制结构函数将会呈现什么环境。
从结构函数界说的代码片段可以看到,傍边利用new操纵符初始化了一个指针str,而隐式的复制结构函数是按值举办复制的,那么对付指针str,将会举办如下复制:
sailor.str = sports.str;
这里复制的不是字符串,而是一个指向字符串的指针!也就是说,我们将获得两个指向同一个字符串的指针!由此会发生的问题将不问可知。当个中一个工具挪用了析构函数之后,其str指向的内存将被释放,这个时候我们假如挪用另一个工具,其str指向的地点数据会是什么?很明明将会呈现不行预料的功效。
#p#副标题#e#
所以由此可见,假如类中包括了利用new初始化的指针成员,该当界说一个复制结构函数,以复制指向的数据,而不是指针,这被称为深度复制。因为默认的浅复制(或成为成员复制)仅浅浅的赋值指针信息。
我们再看以下代码片段,我们稍做修改:
StringBad headline1( " Celery Stalks at Midnight " );
StringBad knot;
knot = headline1;
这里的最后一行将与以上例子有所区别,此刻是将已有工具赋给另一个已有工具,这将会采纳其他操纵,纵然用重载的赋值操纵符。(我们需要知道的是:初始化老是会挪用复制结构函数,而利用=操纵符时也大概挪用赋值操纵符)因为C++答允工具赋值,这是通过自动为类重载赋值操纵符实现的。其原型如下:
Class_name & Class_name:: operator = ( const Class_name & );
它接管并返回一个指向类工具的引用。
与隐式的复制结构函数一样,隐式的工具赋值操纵符也会发生同样的问题,即包括了利用new初始化的指针成员时,只会回收浅复制。所以我们需要利用同样的办理步伐,即界说一个重载的赋值操纵符来实现深度复制。
所以综上所述,假如类中包括了利用new初始化的指针成员,我们应该显式界说一个复制结构函数和一个重载的赋值操纵符来实现其深度复制,制止由此带来的成员复制问题
1. 以下函数哪个是拷贝结构函数,为什么?
X::X(const X&);
X::X(X);
X::X(X&, int a=1);
X::X(X&, int a=1, b=2);
2. 一个类中可以存在多于一个的拷贝结构函数吗?
3. 写出以下措施段的输出功效, 并说明为什么? 假如你都能答复无误的话,那么你已经对拷贝结构函数有了相当的相识。
#include <iostream></iostream>
#include <string></string>
struct X {
template<typename T>
X( T& ) { std::cout << "This is ctor." << std::endl; }
template<typename T>
X& operator=( T& ) { std::cout << "This is ctor." << std::endl; }
};
void main() {
X a(5);
X b(10.5);
X c = a;
c = b;
}
解答如下:
1. 对付一个类X,假如一个结构函数的第一个参数是下列之一:
a) X&
b) const X&
c) volatile X&
d) const volatile X&
且没有其他参数或其他参数都有默认值,那么这个函数是拷贝结构函数。
X::X(const X&); //是拷贝结构函数
X::X(X&, int=1); //是拷贝结构函数
2.类中可以存在高出一个拷贝结构函数,
class X {
public:
X(const X&);
X(X&); // OK
};
留意,假如一个类中只存在一个参数为X&的拷贝结构函数,那么就不能利用const X或volatile X的工具实行拷贝初始化。
class X {
public:
X();
X(X&);
};
const X cx;
X x = cx; // error
假如一个类中没有界说拷贝结构函数,那么编译器会自动发生一个默认的拷贝结构函数。
这个默认的参数大概为X::X(const X&)或X::X(X&),由编译器按照上下文抉择选择哪一个。
默认拷贝结构函数的行为如下:
默认的拷贝结构函数执行的顺序与其他用户界说的结构函数沟通,执行先父类后子类的结构。
拷贝结构函数对类中每一个数据成员执行成员拷贝(memberwise Copy)的行动。
a)假如数据成员为某一个类的实例,那么挪用此类的拷贝结构函数。
b)假如数据成员是一个数组,对数组的每一个执行按位拷贝。
c)假如数据成员是一个数量,如int,double,那么挪用系统内建的赋值运算符对其举办赋值。
3.拷贝结构函数不能由成员函数模版生成。
#p#分页标题#e#
struct X {
template<typename T>
X( const T& ); // NOT copy ctor, T can't be X
template<typename T>
operator=( const T& ); // NOT copy ass't, T can't be X
};
原因很简朴, 成员函数模版并不改变语言的法则,而语言的法则说,假如措施需要一个拷贝结构函数而你没有声明它,那么编译器会为你自动生成一个。 所以成员函数模版并不会阻止编译器生成拷贝结构函数, 赋值运算符重载也遵循同样的法则。(拜见Effective C++ 3edition, Item45)