`
lc52520
  • 浏览: 369296 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

拷贝构造函数/复制构造函数

阅读更多
拷贝构造函数,经常被称作 X(X&),是一种特殊的构造函数,他由编译器调用来完成一些基于同一类的其他对象的构件及初始化。它的唯一的一个参数(对象的引用)是不可变的(因为是const型的)。这个函数经常用在函数调用期间于用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用。
在C++中,下面三种对象需要拷贝的情况。因此,拷贝构造函数将会被调用。
1). 一个对象以值传递的方式传入函数体
2). 一个对象以值传递的方式从函数返回
3). 一个对象需要通过另外一个对象进行初始化
以上的情况需要拷贝构造函数的调用。如果在前两种情况不使用拷贝构造函数的时候,就会导致一个指针指向已经被删除的内存空间。对于第三种情况来说,初始化和赋值的不同含义是构造函数调用的原因。事实上,拷贝构造函数是由普通构造函数和赋值操作赋共同实现的。描述拷贝构造函数和赋值运算符的异同的参考资料有很多。
拷贝构造函数不可以改变它所引用的对象,其原因如下:当一个对象以传递值的方式传一个函数的时候,拷贝构造函数自动的被调用来生成函数中的对象。如果一个对象是被传入自己的拷贝构造函数,它的拷贝构造函数将会被调用来拷贝这个对象这样复制才可以传入它自己的拷贝构造函数,这会导致无限循环。
除了当对象传入函数的时候被隐式调用以外,拷贝构造函数在对象被函数返回的时候也同样的被调用。换句话说,你从函数返回得到的只是对象的一份拷贝。但是同样的,拷贝构造函数被正确的调用了,你不必担心。
如果在类中没有显式的声明一个拷贝构造函数,那么,编译器会私下里为你制定一个函数来进行对象之间的位拷贝(bitwise copy)。这个隐含的拷贝构造函数简单的关联了所有的类成员。许多作者都会提及这个默认的拷贝构造函数。注意到这个隐式的拷贝构造函数和显式声明的拷贝构造函数的不同在于对于成员的关联方式。显式声明的拷贝构造函数关联的只是被实例化的类成员的缺省构造函数除非另外一个构造函数在类初始化或者在构造列表的时候被调用。
拷贝构造函数是程序更加有效率,因为它不用再构造一个对象的时候改变构造函数的参数列表。设计拷贝构造函数是一个良好的风格,即使是编译系统提供的帮助你申请内存默认拷贝构造函数。事实上,默认拷贝构造函数可以应付许多情况。

下面举例说明:

如果不主动编写拷贝构造函数和赋值函数,编译器将以“位拷贝”的方式自动生成缺省的函数。倘若类中含有指针变量,那么这两个缺省的函数就隐含了错误。以类String 的两个对象a,b 为例,假设a.m_data 的内容为“hello”,
b.m_data 的内容为“world”。现将a 赋给b,缺省赋值函数的“位拷贝”意味着执行b.m_data = a.m_data。这将造成三个错误:
一是b.m_data 原有的内存没被释放,造成内存泄露;
二是b.m_data 和a.m_data 指向同一块内存,a 或b 任何一方变动都会影响另一方;三是在对象被析构时,m_data 被释放了两次。
拷贝构造函数和赋值函数非常容易混淆,常导致错写、错用。拷贝构造函数是在对象被创建时调用的,而赋值函数只能被已经存在了的对象调用。以下程序中,第三
个语句和第四个语句很相似,你分得清楚哪个调用了拷贝构造函数,哪个调用了赋
值函数吗?
String a(“hello”);
String b(“world”);
String c = a; // 调用了拷贝构造函数,最好写成 c(a);
c = b; // 调用了赋值函数

本例中第三个语句的风格较差,宜改写成String c(a) 以区别于第四个语句。
9.6 示例:类String 的拷贝构造函数与赋值函数
// 拷贝构造函数
String::String(const String &other)
{
// 允许操作other 的私有成员m_data
int length = strlen(other.m_data);
m_data = new char[length+1];
strcpy(m_data, other.m_data);
}
// 赋值函数
String & String::operate =(const String &other)
{
// (1) 检查自赋值
if(this == &other)
return *this;
// (2) 释放原有的内存资源
delete [] m_data;
// (3)分配新的内存资源,并复制内容
int length = strlen(other.m_data);
m_data = new char[length+1];
strcpy(m_data, other.m_data);
// (4)返回本对象的引用
return *this;
}

类String 拷贝构造函数与普通构造函数的区别是:在函数入口处无
需与NULL 进行比较,这是因为“引用”不可能是NULL,而“指针”可以为NULL。
类String 的赋值函数比构造函数复杂得多,分四步实现:
(1)第一步,检查自赋值。你可能会认为多此一举,难道有人会愚蠢到写出 a = a 这
样的自赋值语句!的确不会。但是间接的自赋值仍有可能出现,例如
// 内容自赋值
b = a;
…
c = b;
…
a = c;
// 地址自赋值
b = &a;
…
a = *b;

也许有人会说:“即使出现自赋值,我也可以不理睬,大不了化点时间让对象复制
自己而已,反正不会出错!”
他真的说错了。看看第二步的delete,自杀后还能复制自己吗?所以,如果发现自
赋值,应该马上终止函数。注意不要将检查自赋值的if 语句
if(this == &other)
错写成为
if( *this == other)
(2)第二步,用delete 释放原有的内存资源。如果现在不释放,以后就没机会了,将造成内存泄露。
(3)第三步,分配新的内存资源,并复制字符串。注意函数strlen 返回的是有效字符串长度,不包含结束符‘\0’。函数strcpy 则连‘\0’一起复制。
(4)第四步,返回本对象的引用,目的是为了实现象 a = b = c 这样的链式表达。注意不要将 return *this 错写成 return this 。那么能否写成return other 呢?效果
不是一样吗?
不可以!因为我们不知道参数other 的生命期。有可能other 是个临时对象,在赋
值结束后它马上消失,那么return other 返回的将是垃圾。

如果我们实在不想编写拷贝构造函数和赋值函数,又不允许别人使用编译器生成的
缺省函数,怎么办?
偷懒的办法是:只需将拷贝构造函数和赋值函数声明为私有函数,不用编写代码。
例如:
class A
{ …
private:
A(const A &a); // 私有的拷贝构造函数
A & operate =(const A &a); // 私有的赋值函数
};

如果有人试图编写如下程序:
A b(a); // 调用了私有的拷贝构造函数
b = a; // 调用了私有的赋值函数

编译器将指出错误,因为外界不可以操作A 的私有函数。

(1)拷贝构造函数和赋值函数的使用比较。

String a("zhangbufeng");
String b("cuixiaoyuan");
String c(a);//对象创建时,使用拷贝构造函数
c=b; //c已经被初始化,从而调用了赋值函数。


(2)
我在vc6.0写程序运行了一下:附源程序和运行结果。

在vc6.0中,File->New->C/C++ source File,然后将此段程序输入进去运行即可!

#include "iostream.h"
#include "stdio.h"
#include "string.h"
class String
{
    public:
   String(const char *str = NULL);      // 普通构造函数
   String(const String &other);      // 拷贝构造函数
   ~ String(void);          // 析构函数
   String & operator =(const String &other); // 赋值函数
    private:
   char    *m_String;     //私有成员,保存字符串
};
String::~String(void)             
{
cout<<"Destructing"< delete [] m_String;                      
   
}
             
String::String(const char *str)      
{
     cout<<"Construcing"< if(str==NULL)                          
{
   m_String = new char[1];    
   *m_String = '\0';                      
}                                        
else
{
   int length = strlen(str);           
   m_String = new char[length+1];        
   strcpy(m_String, str);                
}
}
String::String(const String &other)  
{
cout<<"Constructing Copy"< int length = strlen(other.m_String);  
m_String = new char[length+1];         
strcpy(m_String, other.m_String);         
}
String & String::operator =(const String &other)   
{
cout<<"Operate = Function"<<enl;

//检查自赋值                    
if(this == &other)
    return *this;

    //释放原有的内存资源           
   delete [] m_String;
  
//分配新的内存资源,并复制内容
int length = strlen(other.m_String);
m_String = new char[length+1];         
   strcpy(m_String, other.m_String);
  
//返回本对象的引用            
   return *this;
}
void main()
{
   String a("zhangbufeng");
   String b("cuixiaoyuan");
   String c(a);
   c=b;
}

运行结果如下:Constructing

               Constructing

               Constructing Copy

               Operator = Function

               Destructing

               Destructing

               Destructing
分享到:
评论

相关推荐

    构造函数和复制构造函数

    构造函数和复制构造函数的详细介绍 构造函数是C++中的一种特殊函数,它们...构造函数、复制构造函数和拷贝构造函数是C++中三个基本的函数,它们之间有着紧密的联系,并且在对象的初始化和复制操作中扮演着重要的角色。

    拷贝构造函数.rar

    1. **直接初始化**:当使用现有的对象来创建新对象时,如`Obj obj2(obj1)`,编译器会调用拷贝构造函数来复制`obj1`的所有成员到`obj2`。 2. **隐式类型转换**:如果一个函数或方法期望一个对象作为参数,但传入的是...

    C++类对象的拷贝构造函数

    与普通类型的对象不同,类对象内部结构一般较为复杂,存在各种成员变量,因此需要通过拷贝构造函数来完成整个复制过程。 拷贝构造函数的工作过程可以理解为:当用一个已初始化过了的自定义类类型对象去初始化另一个...

    C 拷贝构造函数.rar

    拷贝构造函数的实现通常包括对类中各个成员的逐个复制,以确保新对象与原对象的数据一致。 拷贝构造函数的三种主要用法包括: 1. **直接初始化**:当创建一个新对象并用已存在对象进行初始化时,拷贝构造函数被调用...

    c++中拷贝构造函数实例

    - **浅复制**:如果类的成员变量是基本类型(如int、double)或指针,拷贝构造函数默认执行浅复制,即只复制指针本身,不复制指针所指向的数据。这样,新旧对象共享同一块内存,修改一个对象可能会影响另一个。 - **...

    拷贝构造函数..........

    拷贝构造函数是C++中一个非常重要的概念,主要用于在对象之间进行深度复制,确保每个对象拥有自己独立的一份数据。拷贝构造函数通常在以下几种情况被调用: 1. 当一个新对象通过已存在的对象初始化时。 2. 当一个...

    C++简单类(构造函数,析构函数以及拷贝构造函数)的实现

    然后,通过拷贝构造函数创建了另一个`cPerson`对象`b`,将`a`的所有属性复制给了`b`。最后,分别打印了两个对象的信息,展示了它们具有相同的属性值。 ### 总结 通过以上分析,我们可以看到,构造函数、析构函数...

    没有可用的复制构造函数或复制构造函数声明

    在C++编程中,"没有可用的复制构造函数或复制构造函数声明"是一个常见的错误,通常出现在尝试复制一个对象,而该对象的类没有定义复制构造函数时。在这个特定的情境中,问题出在一个名为`CArray, int&gt;`的自定义数组...

    C++拷贝构造函数和赋值操作

    ### C++拷贝构造函数与赋值操作详解 在C++编程中,拷贝构造函数与赋值操作是实现类的拷贝管理的关键机制。它们主要用于处理类的对象之间的拷贝和复制过程,尤其是在处理含有动态分配内存的类时尤为重要。 #### ...

    C++实现 类string的 普通构造函数, 拷贝构造函数 析构函数 和赋值函数

    这里的拷贝构造函数确保了`data_`的正确复制,避免了原始对象和副本之间的数据冲突。 **析构函数**: 析构函数在对象生命周期结束时(如离开作用域或显式删除)自动调用,用于清理对象分配的资源。对于`std::string...

    C++面试试题-拷贝构造函数

    然而,默认的拷贝构造函数通常执行的是浅复制(shallow copy),即仅仅复制对象的指针或其他引用,而不是真正地复制其所指向的数据。这种行为在某些情况下可能导致问题,尤其是在处理动态分配的内存时。 #### 题目...

    拷贝构造函数

    拷贝构造函数的存在是为了满足在C++中对象的复制需求,它可以在三个情况下被自动调用:第一种情况是当用类的一个对象去初始化该类的另一个对象时;第二种情况是如果函数的形参是类的对象,调用函数时,进行形参和...

    堆与拷贝构造函数PPT课件.pptx

    在C++编程中,理解堆与拷贝构造函数的概念至关重要,因为它们直接影响到程序的内存管理和对象的复制。本篇主要探讨这两个主题。 首先,让我们了解C++程序中的内存布局。程序的内存通常分为四个主要区域:全局数据区...

    2.4+拷贝构造函数1

    需要注意的是,如果程序员没有自定义拷贝构造函数,C++编译器会提供一个默认的拷贝构造函数,它执行逐个成员的按位复制,适用于基本类型和不可变类型。但对于包含动态分配资源的类,这种默认行为可能不适用,可能...

    C++拷贝构造函数详解1

    如果未定义拷贝构造函数,编译器会提供一个默认的,但这个默认拷贝构造函数只进行浅复制,可能无法处理复杂对象的正确复制。 四、深拷贝与浅拷贝 默认的拷贝构造函数执行的是浅拷贝,即复制指针本身而不复制指针...

    C++拷贝构造函数的介绍及使用

    C++中的拷贝构造函数是编程中不可或缺的一个概念,它在处理对象的复制和赋值操作时起到关键作用。拷贝构造函数是一个特殊类型的构造函数,它的任务是创建一个新对象,这个新对象是已有对象的一个副本。这种功能在...

    详解C++ 拷贝构造函数

    拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于: 通过使用另一个同类型的对象来初始化新创建的对象。 复制对象把它作为参数传递给...

    2.2拷贝构造函数(copyconstuctor)共2页

    拷贝构造函数是C++编程中的一个重要概念,它在对象创建过程中起着关键的作用,特别是在对象复制时。拷贝构造函数是一种特殊的构造函数,它的任务是初始化一个新对象为现有对象的副本。这个过程称为深拷贝或浅拷贝,...

    关于拷贝构造函数和赋值运算符

    在这个例子中,拷贝构造函数将复制常规成员和指针指向的内容,使得新对象独立于原对象。 拷贝构造函数的调用有两种情况:一是在对象声明语句中,例如`CExample theObjtwo = theObjone;`;二是在对象作为函数参数时...

Global site tag (gtag.js) - Google Analytics