【C/C++】C++高级主题之复制构造函数
2014-10-12 10:17
260 查看
复制构造函数
考虑下列Department类型变量的定义:
Department dept=qc;
尽管这个定义看起来像赋值,但operator=函数并没有发挥作用。operator=函数的目的就是用一个已有的对象赋给另外一个对象。然而,此时对象dept还是没有被构造,即指针dept.address只存放了一个随机值。如果回顾operator=函数的实现代码,就会注意到其第一部分代码删除了address指针所指向的原有对象。如果用一个未初始化的对象来执行operator=函数,就会引发致命错误,因为它试图删除一个未被初始化的指针,这将导致程序崩溃或者动态内存冲突。
实际上,编译程序将调用另一个内存管理函数---复制构造函数。它定义怎样将类的一个对象作为另一个对象的副本来构造一个对象。
如果用户没有定义复制构造函数,编译程序将提供一个默认的复制构造函数,它只是把已有对象的数据成员复制给新建对象的对应数据成员。对于类Department来说,其默认复制构造函数应包括如下动作:
dept.name=qc.name;
strcpy(dept.address,qc.address);
但是,该复制构造函数有问题,它将会导致与默认赋值运算符函数同样的错误。所以,必须定义一个复制构造函数,以便创建一个address的一个副本。
下面是Department类的一个复制构造函数:
Department::Department(const Department& b)
{
cout<<"复制构造函数:";
b.print();
name=b.name;
if(b.address==NULL)
address=NULL;
else
{
address=new char[strlen(b.address)+1];
name=b.name;
strcpy(address,b.address);
}
}
赋值运算符函数、复制构造函数和析构函数是较为重要的三个部分。在任何涉及动态内存管理的类中必须实现它们。
正如Marshall Cline所说:“它不仅仅是个好主意,它是法律”。当这个法律变得和税法一样并且被人接受时,遵守起来也就不难了。定义这三个函数时应采用下面的逻辑步骤:
析构函数(Destructor)
释放对象所管理的所有动态内存。
复制构造函数(Copy Constructor)
用显示参数对象的副本来初始化对象。
赋值运算符函数(Assignment Operator)
测试this==&b是否为真。如果为真,则什么也不做。
释放不再需要的对象的动态内存。
将对象设置为显示参数对象的一个副本。
返回*this。
注意:如果是在用户自定义的类中管理动态内存,则需要考虑这三个函数。如果使用类库如vector或list,就不必考虑这些,
因为,这些类都已实现了这三个相应的函数。
下面是类Department中这三个函数的内存管理程序的测试程序。为了测试,内存管理程序将显示跟踪信息。
#include<string>
#include<iostream>
using namespace std;
class Department
{
public:
Department(string _name);
Department(string _name,char* _address);
~Department();
Department& operator=(const Department& b);
Department(const Department &b);//复制构造函数
void print()const;
private:
string name;
char* address;
};
Department::Department(string _name)
{
name=_name;
address=NULL;
cout<<"Constructor:";
print();
}
Department::Department(string _name,char* _address)
{
name=_name;
address=new char[strlen(_address)+1];
strcpy(address,_address);
cout<<"Constructor:";
print();
}
Department::~Department()
{
cout<<"Destructor:";
print();
delete []address;
}
Department::Department(const Department& b)
{
cout<<"复制构造函数:";
b.print();
name=b.name;
if(b.address==NULL)
address=NULL;
else
{
address=new char[strlen(b.address)+1];
name=b.name;
strcpy(address,b.address);
}
}
Department& Department::operator=(const Department& b)
{
cout<<"Assignment:";
print();
cout<<"= ";
b.print();
if(this != &b)
{
name=b.name;
delete[] address;
if(b.address==NULL)
address=NULL;
else
{
address=new char[strlen(b.address)+1];
name=b.name;
strcpy(address,b.address);
}
}
return *this;
}
void Department::print()const
{
cout<<"name="<<name<<",address=";
if(address==NULL)
cout<<"NULL";
else
cout<<address;
cout<<"\n";
}
/*
void fun(Department x)
{
string p=x.name;//error cannot access private variable
}*/
void main()
{
Department shipping("shipping");
Department qc("quality control","china");
Department dept(qc);
dept=shipping;
}
程序运行结果:
程序有三个构造函数调用和三个匹配的析构函数调用。
摘自:C++核心思想 C++高级主题
考虑下列Department类型变量的定义:
Department dept=qc;
尽管这个定义看起来像赋值,但operator=函数并没有发挥作用。operator=函数的目的就是用一个已有的对象赋给另外一个对象。然而,此时对象dept还是没有被构造,即指针dept.address只存放了一个随机值。如果回顾operator=函数的实现代码,就会注意到其第一部分代码删除了address指针所指向的原有对象。如果用一个未初始化的对象来执行operator=函数,就会引发致命错误,因为它试图删除一个未被初始化的指针,这将导致程序崩溃或者动态内存冲突。
实际上,编译程序将调用另一个内存管理函数---复制构造函数。它定义怎样将类的一个对象作为另一个对象的副本来构造一个对象。
如果用户没有定义复制构造函数,编译程序将提供一个默认的复制构造函数,它只是把已有对象的数据成员复制给新建对象的对应数据成员。对于类Department来说,其默认复制构造函数应包括如下动作:
dept.name=qc.name;
strcpy(dept.address,qc.address);
但是,该复制构造函数有问题,它将会导致与默认赋值运算符函数同样的错误。所以,必须定义一个复制构造函数,以便创建一个address的一个副本。
下面是Department类的一个复制构造函数:
Department::Department(const Department& b)
{
cout<<"复制构造函数:";
b.print();
name=b.name;
if(b.address==NULL)
address=NULL;
else
{
address=new char[strlen(b.address)+1];
name=b.name;
strcpy(address,b.address);
}
}
赋值运算符函数、复制构造函数和析构函数是较为重要的三个部分。在任何涉及动态内存管理的类中必须实现它们。
正如Marshall Cline所说:“它不仅仅是个好主意,它是法律”。当这个法律变得和税法一样并且被人接受时,遵守起来也就不难了。定义这三个函数时应采用下面的逻辑步骤:
析构函数(Destructor)
释放对象所管理的所有动态内存。
复制构造函数(Copy Constructor)
用显示参数对象的副本来初始化对象。
赋值运算符函数(Assignment Operator)
测试this==&b是否为真。如果为真,则什么也不做。
释放不再需要的对象的动态内存。
将对象设置为显示参数对象的一个副本。
返回*this。
注意:如果是在用户自定义的类中管理动态内存,则需要考虑这三个函数。如果使用类库如vector或list,就不必考虑这些,
因为,这些类都已实现了这三个相应的函数。
下面是类Department中这三个函数的内存管理程序的测试程序。为了测试,内存管理程序将显示跟踪信息。
#include<string>
#include<iostream>
using namespace std;
class Department
{
public:
Department(string _name);
Department(string _name,char* _address);
~Department();
Department& operator=(const Department& b);
Department(const Department &b);//复制构造函数
void print()const;
private:
string name;
char* address;
};
Department::Department(string _name)
{
name=_name;
address=NULL;
cout<<"Constructor:";
print();
}
Department::Department(string _name,char* _address)
{
name=_name;
address=new char[strlen(_address)+1];
strcpy(address,_address);
cout<<"Constructor:";
print();
}
Department::~Department()
{
cout<<"Destructor:";
print();
delete []address;
}
Department::Department(const Department& b)
{
cout<<"复制构造函数:";
b.print();
name=b.name;
if(b.address==NULL)
address=NULL;
else
{
address=new char[strlen(b.address)+1];
name=b.name;
strcpy(address,b.address);
}
}
Department& Department::operator=(const Department& b)
{
cout<<"Assignment:";
print();
cout<<"= ";
b.print();
if(this != &b)
{
name=b.name;
delete[] address;
if(b.address==NULL)
address=NULL;
else
{
address=new char[strlen(b.address)+1];
name=b.name;
strcpy(address,b.address);
}
}
return *this;
}
void Department::print()const
{
cout<<"name="<<name<<",address=";
if(address==NULL)
cout<<"NULL";
else
cout<<address;
cout<<"\n";
}
/*
void fun(Department x)
{
string p=x.name;//error cannot access private variable
}*/
void main()
{
Department shipping("shipping");
Department qc("quality control","china");
Department dept(qc);
dept=shipping;
}
程序运行结果:
程序有三个构造函数调用和三个匹配的析构函数调用。
摘自:C++核心思想 C++高级主题
相关文章推荐
- C++高级主题之复制构造函数
- <c++ primer>--第五部分 高级主题
- C++ 高级主题之模板特化
- <c++ primer>--第五部分 高级主题
- C++高级主题之模板特化
- 用C++的高级模版特性实现一个不需要IDL的RPC
- 系统高级主题
- C++主题年技巧积累#1——UltraEdit的代码美化
- Pro visual c++/cli and .net 2.0 platform2 学习笔记(10 第四章 高级C++/CLI)
- 4道稍微高级点的c++面试题,供初学者一窥门径
- C++主题年技巧积累#2——我被static撞了一下腰
- 高质量C++/C编程指南 -- 第8章 C++函数的高级特性
- C++主题年技巧积累#1——UltraEdit的代码美化
- 跟风C++主题年:从虚析构函数想到的内存基本模型
- C++主题年技巧积累#1——UltraEdit的代码美化
- C++编程规范---第8章 C++函数的高级特性
- 高质量C++/C编程指南 -- 第8章 C++函数的高级特性
- C++主题年技巧积累#2——我被static撞了一下腰
- 高质量C++/C编程指南 -- 第8章 C++函数的高级特性
- VB编程基础--高级变量主题