A(const A&); //默认拷贝构造函数
A& operater = (const A& a); //默认赋值函数
【弊端】:若类中包含指针成员或引用成员,这两个默认的函数可能隐含错误。
(1) 原有的内存没有释放,造成内存泄漏;
(2) 使两个对象中的指针成员指向同一块内存,任何一方变动都会影响另一方;
(3) 在对象析构时,delete两次。
拷贝构造函数:在创建对象,并用另一个已经存在的对象来初始化它时调用。
如:String a("hello"); //调用带参数的构造函数
String b(a); //或是String b = a,调用拷贝构造函数
赋值函数:把一个对象赋值给另一个已经存在的对象,使得已经存在的那个对象和源对象具有相同的状态。
如:String c;
c = a;
类String拷贝构造函数在函数入口处不用与NULL比较,因为引用不可能是NULL,而指针可以为NULL,所以在默认构造函数中需与NULL比较。
例1:类String的拷贝构造函数和拷贝赋值函数
String::String(const String& other)
{
int length = strlen(other.m_data);
m_data = new char[length + 1];
strcpy(m_data, other.m_data);
m_size = length;
}
String& String::operator = (const String& other)
{
//(1)检查自赋值
if (this != &other) //地址相等才认为是同一对象,不能错写成if (*this = other)
{
//(2)分配新的内存空间,并拷贝内容
char* temp = new char[strlen(other.m_data) + 1];
strcpy(temp, other.m_data ); //连'/0'一起拷贝
//(3)释放原有的内存资源
delete [ ] m_data;
m_data = temp;
m_size = strlen(other.m_data);
}
//(4)返回本对象的引用
return *this; //返回本对象的引用,目的是为了实现a=b=c这样的链式表达式
}
如果先把原有的内存释放,如果后来内存重分配失败,就惨了!所以,先分配内存给一个临时指针,万一分配失败也不会改变this对象。
如果我们不想编写拷贝构造函数和拷贝赋值函数(不想拷贝对象),又不允许使用编译器自动生成的默认函数,只需将拷贝构造函数和拷贝赋值函数声明为private,并且不实现它们。
基类的构造函数、析构函数和赋值函数不能被派生类继承。如果存在类继承关系,则在编写基本函数时应该注意:
(1) 派生类的构造函数应该其初始化列表里显式地调用基类构造函数;
(2) 如果基类是多态类(包括虚函数的类),则必须把基类的析构函数定义为虚函数,这样可以像其他函数一样实现动态绑定,否则可能会造成内存泄漏;
如:Base *pB = new Derived;
是虚函数,析构时先调用Derived::~Derived(),再调用Base::~Base()
不是虚函数,则直接调用Base::~Base(),因此派生类对象的内存不会释放,造成内存泄漏
(3) 在编写派生类的赋值函数时,要记得对基类的数据成员重新赋值,可以通过调用基类的赋值函数来实现。
如:
Derived& Derived::operater = (const Derived& other)
{
//(1)检查自赋值
if (this != &other)
{
//(2)对基类的数据成员重新赋值
Base::operator = (other);
//(3)对派生类的数据成员赋值
m_x = other.m_x;
m_y = other.m_y;
m_z = other.m_z;
}
//(4)返回本对象的引用
return *this;
}
------------------------
文章转自互联网。。。