• 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++高级主题

  • 相关阅读:
    MySQL2
    MySQL1
    并发编程三
    并发编程二
    并发编程一
    网络基础知识
    反射和单例
    Spring mvc文件上传实现
    SpringMVC系列之SpringMVC快速入门 MVC设计模式介绍+什么是SpringMVC+ SpringMVC的作用及其基本使用+组件解析+注解解析
    java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderL
  • 原文地址:https://www.cnblogs.com/wintergrass/p/2051515.html
Copyright © 2020-2023  润新知