C++复制构造函数分析
来源:互联网 发布:切换城市js代码 编辑:程序博客网 时间:2024/05/23 07:26
复制构造函数(copy constructor)是一种特殊的构造函数,具有单个形参,该形参(通常也是const)是该类引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数,当将该类对象传递给函数或函数返回该类型的对象时,将隐式的使用复制构造函数。如果没有类没有定义复制构造函数,由编译自动为类合成复制构造函数。
1.复制构造可以编译器隐式调用,具体可分为以下几种情况
(1) 根据另一个同类型的对象显示或隐式的初始化一个对象,通过一个同类对象初始化一个新对象是调用其复制构造函数完成的。
(2) 一个对象,将它作为实参传给一个函数,如果函数(包括成员函数)的参数为类类形的,那么调用过程中先通过复制构造函数构造临时副本,把参数传入函数的栈区,等函数调用返回时再调用类的析构函数,销毁临时副本对象。
(3) 从函数返回一个复制对象, 与实参传入函数相同。
(4) 初始化顺序容器的元素
复制构造函数可用于初始化顺序容器中的元素,
如vector<Classname>svec(10);
编译器使用Classname的默认构造函数创建一个临时对象,后再用复制构造函数来初始化svec元素,每元素重复这个过程。
(5) 根据元素初始化式列表初始化数组
定义类类形数组时,如果没有为类类形数组提供初始化元素,则由默认构造函数初始化一个临时对象后,再用复制构造函数初始化数组元素,如果指定了适当的类型元素,则通过复制构造函数初始化元素。
以下用一个简单的Book类来说明以上各种情况
- class Book
- {
- public:
- Book(string name,int num):m_strName(name),m_nNum(num),
- m_ptPrice(new double(10.0))
- {
- cout<<m_strName<<" Constructor"<<endl;
- }
- Book(string name = "default"):m_strName(name)
- {
- m_nNum = 100;
- m_ptPrice = new double(999);
- cout<<m_strName<<" Constructor"<<endl;
- }
- Book(const Book &b)
- {
- m_nNum = b.m_nNum;
- m_strName = b.m_strName;
- m_ptPrice = new double(*b.m_ptPrice);
- cout<<m_strName<<" copy constructor..."<<endl;
- }
- const Book operator=(const Book b)
- {
- m_nNum = b.m_nNum;
- m_strName = b.m_strName;
- m_ptPrice = new double(*b.m_ptPrice);
- cout<<m_strName<<" operator= call..."<<endl;
- return *this;
- }
- void DisplayBookInf()
- {
- cout<<"BookName: "<<m_strName
- <<" Num: "<<m_nNum<<" Price: "<<*m_ptPrice<<endl;
- }
- ~Book()
- {
- //delete m_ptPrice;
- cout<<m_strName<<" Dstructor"<<endl;
- }
- public:
- string m_strName;
- int m_nNum;
- double *m_ptPrice;
- };
下面是各种情况下实现
- int _tmain(int argc, _TCHAR* argv[])
- {
- cout<<"语句:Book b1(\"c++ primer\",100);"<<endl;
- Book b1("c++ primer",100);//两个参数的构造函数调用
- cout<<"语句:Book b2 = b1;"<<endl;
- Book b2 = b1;//复制构造函数调用
- cout<<"语句: b3(b2);"<<endl;
- Book b3(b2);//复制构造函数调用
- cout<<"语句:Book b4 = \"thinking in c++\";"<<endl;
- Book b4 = "thinking in c++";//一个参数构造函数调用
- cout<<"语句:GetBook(b1)"<<endl;
- GetBook(b1);//复制构造函数与析构函数调用各调用两次
- cout<<"TotalValue(b4) "<<endl;
- TotalValue(b4);//引用调用不会有构造函数与析构函数调用
- cout<<"语句:vector<Book> vec(2)"<<endl;
- vector<Book> vec(2);//先调用默认构造函数,再调用复制构造函数,两次
- cout<<"语句:BookArrary bArray[2] = {Book(\"thinking in jave\"),b4};"<<endl;
- BookArrary bArray[2] = {Book("thinking in jave"),b4};
- cout<<"语句:b4 = b1;"<<endl;
- b4 = b1;//因为Book重载了"="所以这里调用的是operator =(const Book b)
- cout<<"对象析构;"<<endl;
- return 0;
- }
运行结果
- 语句:Book b1("c++ primer",100);
- c++ primer Constructor
- 语句:Book b2 = b1;
- c++ primer copy constructor...
- 语句: b3(b2);
- c++ primer copy constructor...
- 语句:Book b4 = "thinking in c++";
- thinking in c++ Constructor
- 语句:GetBook(b1)
- c++ primer copy constructor...
- c++ primer copy constructor...
- c++ primer Dstructor
- c++ primer Dstructor
- TotalValue(b4)
- 引用调用
- 语句:vector<Book> vec(2)
- default Constructor
- default copy constructor...
- default Dstructor
- default Constructor
- default copy constructor...
- default Dstructor
- 语句:BookArrary bArray[2] = {Book("thinking in jave"),b4};
- thinking in jave Constructor
- thinking in jave copy constructor...
- thinking in jave Dstructor
- thinking in c++ copy constructor...
- 语句:b4 = b1;
- c++ primer copy constructor...
- c++ primer operator= call...
- c++ primer copy constructor...
- c++ primer Dstructor
- c++ primer Dstructor
- 对象析构;
- thinking in c++ Dstructor
- thinking in jave Dstructor
- default Dstructor
- default Dstructor
- c++ primer Dstructor
- c++ primer Dstructor
- c++ primer Dstructor
- c++ primer Dstructor
- 请按任意键继续. . .
2.类成员变量含有指针的复制
如果我们的类没有定义复制构造函数,则编译器自动合成一个复制构造函数。该复制构造函数在以上各种情况中被调用。默认的复制构造函数把一个类对象的成员变量逐个到另外一个类对象中。如果该对象有指针指向动态内存分配,则通过复制把指针变量的数值复制给新对象相应的指针变量,将会造成两个类对象同时指向同一个内存空间,如果其中有一个对象已经析构,则其指针变量指向的内存空间就会销毁,那么另外一个对象指针成员会指向已经销毁的空间,造成该对象的指针变量悬空,指向非法的空间。
如何解决上述问题呢,一般可以通过两种方法来实现,一是通过深度复制,即在类的复制构造函数中为类的指针变量动态分配内存空间,另一是通过定义智能指针类。
(1)第一种方法:深度复制
复制构造定义如下:
- Book(const Book &b)
- {
- m_nNum = b.m_nNum;
- m_strName = b.m_strName;
- m_ptPrice = new double(*b.m_ptPrice);//通过操作符new为指针分配内存空间
- //cout<<m_strName<<" copy constructor..."<<endl;}
在Book类中,为其定义了复制构造函数,并在该函数中为类的指针变量重新分配了内存空间,这样就不会将成将造成指针悬空。则通过Book b2(b1)将会造成下图的内存布局:
通过以上分析可知,通过深度复制可以解决指针悬空问题,但也有个缺点:如果动态分配的空间很大的话,通过复制构造定义多个对象则会造成很大空间浪费,因为它们动态内存空间里的数据都是一样的。所以下面介绍另外一种解决方法,智能指针。
(2)智能指针
定义智能指针有通用技术是采用使用计数
首先定义计数类:
- class UseCount
- {
- frend class ClassPT;
- int*m_ptValue;
- int m_nCount;
- UseCount(double *pVale):m_ptValue(pValue),m_nCount(1){}
- ~UseCount(){ delete m_ptValue;}
- };
- ClassPT类作为UseCount,故ClassPT类可以访问UseCount计数类的所有成员变量。有了引用计数类之后 ,只需重写复制构造函数及”=”操作符即可。
- class ClassPT
- {
- public:
- ClassPT(int *pt, int val):m_ptr(new UseCount(p)),m_nValue(val){}
- ClassPT(const ClassPT &orig):m_ptr(orig.ptr),m_nValue(orig.m_nValue)
- {
- ++m_ptr. nCount;
- }
- ClassPT &operator=(const ClassPT &orig)
- {
- ++orig.m_ptr->m_nCount;
- if(--m_ptr->m_nCount == 0)
- {
- delete m_ptr;
- }
- }
- m_ptr = orig.m_ptr;
- m_nValue =orig.nValue;
- return *this;
- }
- private:
- UseCount m_ptr;
- int m_nValue;
- };
ClassPT对象构造时内存模型
ClassPT对象复制构造时内存模型
- C++: 复制构造函数
- C++复制构造函数分析
- C++例题分析2,复制构造函数
- c++-拷贝构造函数(复制构造函数)
- 复制构造函数与赋值重载(c++)
- c++primer 3/12----复制构造函数
- C++_避免调用复制构造函数
- C++(22)复制构造函数
- C++:复制构造函数___浅拷贝
- C++:复制构造函数的应用
- 【C++】This指针和复制构造函数
- 【学习C++】学习C++ -> 复制构造函数
- 【C++】拷贝构造函数之浅复制与深复制
- 构造函数 复制构造函数
- C++_浅层复制构造函数和深层复制构造函数
- 【C/C++】C++高级主题之复制构造函数
- C/C++——构造函数、复制构造函数和析构函数的执行时刻
- 深复制-复制构造函数
- iPhone的Push(推送通知)功能原理浅析
- Mathematica中如何把图片贴到球面或圆柱面上?
- htmlentities()
- 陈坤极可能走进北大
- htmlspecialchars()
- C++复制构造函数分析
- CSS中背景图片定位方法
- strip_tags()
- uva10986(最短路径)
- 解决真机调试时Eclipse DDMS上打不开/data目录的问题
- android TextView 的垂直滚动
- md5()
- UVA 11400 Lighting System Design
- 素数求和