拷贝构造函数 订阅
拷贝构造函数,又称复制构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其形参必须是引用,但并不限制为const,一般普遍的会加上const限制。此函数经常用在函数调用时用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用。 展开全文
拷贝构造函数,又称复制构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其形参必须是引用,但并不限制为const,一般普遍的会加上const限制。此函数经常用在函数调用时用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用。
信息
属    于
一种特殊的构造函数
应用学科
计算机科学、测绘科学
形参形式
引用
中文名
拷贝构造函数
外文名
Copy Constructor
又    称
复制构造函数
拷贝构造函数概念
在C++中,下面三种对象需要调用拷贝构造函数(有时也称“复制构造函数”):1) 一个对象作为函数参数,以值传递的方式传入函数体;2) 一个对象作为函数返回值,以值传递的方式从函数返回;3) 一个对象用于给另外一个对象进行初始化(常称为赋值初始化);如果在前两种情况不使用拷贝构造函数的时候,就会导致一个指针指向已经被删除的内存空间。对于第三种情况来说,初始化和赋值的不同含义是拷贝构造函数调用的原因。事实上,拷贝构造函数是由普通构造函数和赋值操作符共同实现的。描述拷贝构造函数和赋值运算符的异同的参考资料有很多。通常的原则是:①对于凡是包含动态分配成员或包含指针成员的类都应该提供拷贝构造函数;②在提供拷贝构造函数的同时,还应该考虑重载"="赋值操作符号。拷贝构造函数必须以引用的形式传递(参数为引用值)。其原因如下:当一个对象以传递值的方式传一个函数的时候,拷贝构造函数自动的被调用来生成函数中的对象。如果一个对象是被传入自己的拷贝构造函数,它的拷贝构造函数将会被调用来拷贝这个对象这样复制才可以传入它自己的拷贝构造函数,这会导致无限循环直至栈溢出(Stack Overflow)。除了当对象传入函数的时候被隐式调用以外,拷贝构造函数在对象被函数返回的时候也同样的被调用。
收起全文
精华内容
下载资源
问答
  • 拷贝构造函数

    千次阅读 2019-09-08 09:52:47
    拷贝构造函数即系统自带的拷贝构造函数,当程序将一个已经定义的对象数据给另一个对象作为初始值时,并且程序为自定义拷贝构造函数,系统就会自动调用默认拷贝构造函数. 形式: 自定义拷贝构造函数(深拷贝) Human...

    一. 什么是拷贝构造函数:

    1. 默认拷贝构造函数(浅拷贝):
      浅拷贝构造函数即系统自带的拷贝构造函数,当程序将一个已经定义的对象数据给另一个对象作为初始值时,并且程序为自定义拷贝构造函 数,系统就会自动调用默认拷贝构造函数.
      调用形式 :
    class Human {
    public:
    	Human();
    	string getName();
    private:
    	string name = "张三";
    };
    Human::Human() {
    	name = "无名";
    }
    int main() {
    	Human h1;				//调用自定义的构造函数
    	Human h2 = h1;		//调用拷贝构造函数
    	Human h3(h1);		//调用拷贝构造函数
    	return 0;
    }
    
    1. 自定义拷贝构造函数(深拷贝):
      深拷贝构造函数即程序员自己定义的拷贝构造函数.
      调用形式:
    class Human {
    public:
    	Human();
    	Human(Human&);
    	string getName();
    private:
    	string name = "张三";
    };
    Human::Human() {
    	name = "无名";
    }
    Human::Human(Human&other) {
    	cout << "调用拷贝构造函数" << endl;
    	name = other.name;
    }
    int main() {
    	Human h1;				//调用自定义的构造函数
    	Human h2 = h1;		//调用拷贝构造函数
    	Human h3(h1);		//调用拷贝构造函数
    	return 0;
    }
    

    二.为什么要自己定义拷贝构造函数

    1. 默认拷贝构造函数(浅拷贝):
      指针拷贝时,拷贝对象h2的里面指针数据指向被拷贝函数(h1),因此当h1的addr改变时,h2的addr也随之改变.
      图1.默认拷贝构造函数
    2. 自定义拷贝构造函数(深拷贝):
      为拷贝对象h2创建独立的内存,指针指向这块内存,即使h1改变,h2也不会随之变.
      图2.自定义拷贝构造函数
    3. 区别:
      默认拷贝函数中的h2的指针变量addr会会随着h1的改变而改变,自定义拷贝函数为h2的addr分配了一块堆地址,因此h2不会随着h1的改变而改变.所以默认拷贝函数存在很大的安全隐患.
    #include<iostream>
    #include<string>
    #include<Windows.h>
    using namespace std;
    #define ADDR_LEN 64
    class Human {
    public:
    	Human();
    	Human(int, int);
    	Human(Human&);
    	void description();
    	string getName();
    	int getAge();
    	int getSalary();
    	void setAddr(char *);//修改地址
    private:
    	string name = "张三";
    	int age = 18;			//类内初始值,c++11才支持
    	int salary = 1800;
    	char *addr="China";			//地址
    };
    Human::Human() {
    	age = 18;
    	salary = 1800;
    	name = "无名";
    	addr = new char[ADDR_LEN];
    	strcpy_s(addr,ADDR_LEN,"China");
    }
    Human::Human(int age, int salary) {
    	this->age = age;
    	this->salary = salary;
    	addr = new char[ADDR_LEN];
    	strcpy_s(addr, ADDR_LEN, "China");
    }
    Human::Human(Human&other) {//相当于const Human &other=h1;
    	cout << "调用拷贝构造函数" << endl;
    	name = other.name;
    	age = other.age;
    	salary = other.salary;
    }
    void Human::description() {
    	cout << "age:" << age << "name:" << name << "salary:" << salary << "addr:" << addr << endl;
    }
    string Human::getName() {
    	return name;
    }
    int Human::getAge() {
    	return age;
    }
    int Human::getSalary() {
    	return salary;
    }
    void Human::setAddr(char *addr) {
    	if (!addr) {
    		return;
    	}
    	strcpy_s(this->addr,ADDR_LEN,addr);
    }
    int main() {
    	Human h1(25, 28000);//调用自定义的构造函数
    	Human h2 = h1;		//调用拷贝构造函数
    	Human h3(h1);		//调用拷贝构造函数
    	h1.description();
    	h2.description();
    	h3.description();
    	h1.setAddr("美国");
    	//修改地址之后<<endl;
    	h1.description();
    	h2.description();
    	h3.description();
    	system("pause");
    	return 0;
    }
    

    三.拷贝构造函数调用时机

    1. 创建一个对象的时候同时用另外一个对象创建初始值:Human h2=h1; Human h2(h1);
    2. 调用函数时,实参是对象,形参不是引用类型
      如果函数的形参是引用类型,就不会调用拷贝构造函数(要在函数后加const,防止函数被
      其他程序员修改,使程序更加安全,)

      如:
    void showMsg(Human man) {
    	man.description();
    }
    

    改为:

    void showMsg(const Human &man) {
    	man.description();
    }
    
    1. 函数的返回类型是类,而且不是引用类型
      如果函数返回值是引用类型,就不会调用拷贝构造函数
      如:
    Human getBetterMan(const Human &man1, const Human &man2) {
    	if (man1.getSalary()>man2.getSalary()) {
    		return man1;
    	}
    	else{
    		return man2;
    	}
    }
    

    改为:

    const Human &getBetterMan(const Human &man1, const Human &man2) {
    	if (man1.getSalary()>man2.getSalary()) {
    		return man1;
    	}
    	else{
    		return man2;
    	}
    }
    
    1. 对象数组的初始化类表中,使用对象
      如:
    Human f1, f2, f3, f4;
    Human F4[4] = {f1,f2,f3,f4};
    

    源代码:

    #include<iostream>
    #include<string>
    #include<Windows.h>
    using namespace std;
    #define ADDR_LEN 64
    class Human {
    public:
    	Human();
    	Human(int, int);
    	Human(const Human&);
    	void description()const;
    	string getName() const;
    	int getAge() const;
    	int getSalary() const;
    	void setAddr(char *);//修改地址
    private:
    	string name = "张三";
    	int age = 18;			//类内初始值,c++11才支持
    	int salary = 1800;
    	char *addr = "China";			//地址
    };
    Human::Human() {
    	age = 18;
    	salary = 1800;
    	name = "无名";
    	addr = new char[ADDR_LEN];
    	strcpy_s(addr, ADDR_LEN, "China");
    }
    Human::Human(int age, int salary) {
    	cout << "调用自定义构造函数" << endl;
    	this->age = age;
    	this->salary = salary;
    	addr = new char[ADDR_LEN];
    	strcpy_s(addr, ADDR_LEN, "China");
    }
    Human::Human(const Human&other) {//相当于const Human &other=h1;
    	cout << "调用拷贝构造函数" << endl;
    	name = other.name;
    	age = other.age;
    	salary = other.salary;
    	//为addr分配一块独立的内存,进行深拷贝
    	addr = new char[ADDR_LEN];
    	strcpy_s(addr, ADDR_LEN, other.addr);
    }
    void Human::description() const {
    	cout << "age:" << age << "name:" << name << "salary:" << salary << "addr:" << addr << endl;
    }
    string Human::getName()const {
    	return name;
    }
    int Human::getAge() const {
    	return age;
    }
    int Human::getSalary() const {
    	return salary;
    }
    void Human::setAddr(char *addr) {
    	if (!addr) {
    		return;
    	}
    	strcpy_s(this->addr, ADDR_LEN, addr);
    }
    /*
    	void showMsg(Human man) {
    	man.description();
    	}
    	1:调用函数 showMsg(h1);
    	在进行参数传递时,自动执行:Human man=h1;
    	在Human前加const,防止其他程序员通过该函数修改类的数据(增加程序的安全性)
    	但是在Human前加上const后,该引用就无法调用该类的方法,所以在定义方法后面加上const
    */
    void showMsg(const Human &man) {
    	man.description();
    }
    /*
    	Human getBetterMan(const Human &man1, const Human &man2) {
    	if (man1.getSalary()>man2.getSalary()) {
    	return man1;
    	}
    	else{
    	return man2;
    	}
    	}
    	2:函数的返回类型是类,而且不是引用类型
    	为了避免使用拷贝构造函数将getBetterMan设为引用类型,但是要与(const Human &man1)类型保持一致,
    	要在开头添加const,
    */
    const Human &getBetterMan(const Human &man1, const Human &man2) {
    	if (man1.getSalary()>man2.getSalary()) {
    		return man1;
    	}
    	else {
    		return man2;
    	}
    }
    int main() {
    	Human f1, f2, f3, f4;
    	/*	Human F4[4] = {f1,f2,f3,f4};
    		3.相当于F[1]=f1,F[2]=f2,F[3]=f3,F[4]=f4;
    	*/
    	Human F4[4] = {f1,f2,f3,f4};
    	system("pause");
    	return 0;
    }
    
    展开全文
  • 拷贝构造函数,默认拷贝构造函数 1.c++的默认拷贝构造函数,从深度拷贝和浅拷贝说起 c++类的默认拷贝构造函数的弊端 c++类的中有两个特殊的构造函数,(1)无参构造函数,(2)拷贝构造函数。它们的特殊之处在于: (1)...

    拷贝构造函数,默认拷贝构造函数
    1.c++的默认拷贝构造函数,从深度拷贝和浅拷贝说起

    1. c++类的默认拷贝构造函数的弊端
      c++类的中有两个特殊的构造函数,(1)无参构造函数,(2)拷贝构造函数。它们的特殊之处在于:
      (1)当类中没有定义任何构造函数时,编译器会默认提供一个无参构造函数且其函数体为空;
      (2)当类中没有定义拷贝构造函数时,编译器会默认提供一个拷贝构造函数,进行成员变量之间的拷贝。(这个拷贝操作是浅拷贝)
      class TestCls{
      public:
      int a;
      int *p;

    public:
    TestCls() //无参构造函数
    {
    std::cout<<“TestCls()”<<std::endl;
    p = new int;
    }

    ~TestCls()     //析构函数
    {
        delete p;   
        std::cout<<"~TestCls()"<<std::endl;
    }
    

    };
    int main(void)
    {
    TestCls t;
    return 0;
    }
    编译运行确实不会出错:

    类在我们没有定义拷贝构造函数的时候,会默认定义默认拷贝构造函数,也就是说可以直接用同类型的类间可以相互赋值、初始化:
    int main(void)
    { TestCls t1; TestCls t2 = t1; //效果等同于TestCls t2(t1); return 0; }
    这样结果就会出错。

    原因就在于,默认的拷贝构造函数实现的是浅拷贝。
    2.深度拷贝和浅拷贝
    深度拷贝和浅拷贝在c语言中就经常遇到的了,在这里我简单描述。
    一般的赋值操作是深度拷贝:

    //深度拷贝
    int a = 5;
    int b = a;
    简单的指针指向,则是浅拷贝:
    //浅拷贝
    int a = 8;
    int *p;
    p = &a;

    char* str1 = “HelloWorld”;
    char* str2 = str1;
    将上面的浅拷贝改为深度拷贝后:
    //深度拷贝
    int a = 8;
    int *p = new int;
    *p = a;

    char* str1 = “HelloWorld”;
    int len = strlen(str1);
    char *str2 = new char[len];
    memcpy(str2, str1, len);
    能看得出深度拷贝和浅拷贝的差异。拷贝者和被拷贝者若是同一个地址,则为浅拷贝,反之为深拷贝。 
    以字符串拷贝为例,浅拷贝后,str1和str2同指向0x123456,不管哪一个指针,对该空间内容的修改都会影响另一个指针。

    深拷贝后,str1和str2指向不同的内存空间,各自的空间的内容一样。因为空间不同,所以不管哪一个指针,对该空间内容的修改都不会影响另一个指针。

    1. 解决默认拷贝构造函数的弊端
      类的默认拷贝构造函数只会用被拷贝类的成员的值为拷贝类简单初始化,也就是说二者的p指针指向的内存空间是一致的。以前面TestCls可以知道,编译器为我们默认定义的拷贝构造函数为:
      TestCls(const TestCls& testCls)
      { a = testCls.a; p = testCls.p; //两个类的p指针指向的地址一致。 }
      main函数将要退出时,拷贝类t2的析构函数先得到执行,它把自身p指向的堆空间释放了;接下来,t1的析构函数得到调用,被拷贝类t1的析构函数得到调用,它同样要去析构自身的p指向指向的堆空间,但是该空间和t2类中p指向的空间一样,造成重复释放,程序运行崩溃。
      解决办法就是自定义拷贝构造函数;
      class TestCls{
      public:
      int a;
      int *p;

    public:
    TestCls()
    {
    std::cout<<“TestCls()”<<std::endl;
    p = new int;
    }

    TestCls(const TestCls& testCls)
    {
        cout<<"TestCls(const TestCls& testCls)"<endl;
        a = testCls.a;
        //p = testCls.p;
        p = new int;
        *p = *(testCls.p);      //为拷贝类的p指针分配空间,实现深度拷贝
    }
    
    ~TestCls()
    {
        delete p;   
      cout<<"~TestCls()"<<endl;
    }
    

    };

    int main(void)
    {
    TestCls t1;
    TestCls t2 = t1;
    return 0;
    }

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 18,193
精华内容 7,277
关键字:

拷贝构造函数