- 浏览: 440198 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
su6838354:
我有点疑问啊,thread1中的i自增的慢的话,thread2 ...
浅析pthread_cond_wait -
zeronever:
请问pthread_cond_signal有解锁操纵吗?我在p ...
浅析pthread_cond_wait -
paladin1988:
你这帖子真心不错。。
浅谈bitmap算法 -
parabellum_sky:
昨天还有个姑娘让我去考我说会考虑
个人日志
构造函数,析构函数和赋值操作符
几乎所有的对象都需要构造函数、拷贝构造函数、析构函数和赋值构造函数,但问题是何时需要自己定义这些函数,何时调用默认的这些函数?这也是所谓的“浅拷贝”和“深拷贝”的问题。
当类的数据成员有指针,需要动态的为指针申请内存时,这时就需要自己定义拷贝构造函数和赋值构造函数,这就是所谓的“深拷贝”。
当类的数据成员没有指针,不需要动态的为指针申请内存时,不需要定义自己版本的拷贝和赋值构造函数,直接调用默认的即可,这就是所谓的“浅拷贝”。
为何这种情况需要定义自己的拷贝构造函数和赋值构造函数?看下面的例子:
#include <iostream> using namespace std; // 一个很简单的TString类 class TString { public: TString(const char *value); ~TString(); void Print(); private: char *data; }; TString::TString(const char *value) { if (value) { data = new char[strlen(value) + 1]; strcpy(data, value); } else { data = new char[1]; *data = '\0'; } cout << "Call Constructor !\n"; } inline TString::~TString() { delete [] data; cout << "Call Destructor !\n";} void TString::Print() { cout << "The content is :" << data << endl; } void donothing(const TString &localstring) { cout << "Call donothing function!\n";} int main() { TString a("hello"); TString b("world"); //b = a; b.Print(); a.Print(); }
输出为:
Call Constructor !
Call Constructor !
The content is :world
The content is :hello
Call Destructor !
Call Destructor !
但是如果定义:b = a; 就会提示错误,为何呢?类本身不是提供默认的赋值构造函数吗,为何还会出错?
如果b = a;而没有定义自己的赋值构造函数,TString b("world");为b对象申请的内存段将丢失,也即没有任何指针指向这个内存段,这就是所谓的内存泄露。而b = a;会通过默认的赋值构造函数直接把指针a所指向的地址赋值给b,那么b和a指向同一段地址,当a和b离开生存空间时会调用类的析构函数来删除申请的地址,比如当b删除了b.data所指向的地址后,a对象也会调用析构函数删除a.data指向的地址,但此地址已经被b对象删除,用delete删除一个已经被删除的指针,其结果是不可预料的。
这就是为什么当类的数据成员是指针时,需要字定义拷贝构造函数和赋值构造函数。
又如:
TString s = "the truth is out there";
donothing(s);
如果没有定义自己的拷贝构造函数,会出现同样的问题,说明如下:
一切好象都很正常。但因为被传递的localstring是一个值,它必须从s通过(缺省)拷贝构造函数进行初始化。于是localstring拥有了一个s内的指针的拷贝。当donothing结束运行时,localstring离开了其生存空间,调用析构函数。其结果也将是:s包含一个指向localstring早已删除的内存的指针。
解决这类指针混乱问题的方案在于,只要类里有指针时,就要写自己版本的拷贝构造函数和赋值操作符函数。在这些函数里,你可以拷贝那些被指向的数据结构,从而使每个对象都有自己的拷贝;或者你可以采用某种引用计数机制(见条款 m29)去跟踪当前有多少个对象指向某个数据结构。引用计数的方法更复杂,而且它要求构造函数和析构函数内部做更多的工作,但在某些(虽然不是所有)程序里,它会大量节省内存并切实提高速度。
在看下面的例子:
#include <iostream> using namespace std; class CSomething { public: int a; int b; public: CSomething(int a, int b) { cout << "Call CSomething Constructor !" << endl; this->a = a; this->b = b; } ~CSomething() { cout << "Call the CSomething Destructor!" << endl; } }; class CA { private: CSomething* sth; // 以指针形式存在的成员变量 public: CA(CSomething* sth) { cout << "Call CA Constructor!" << endl; this->sth = new CSomething(sth->a, sth->b); } ~CA() { cout << "In the destructor of class CA..." << endl; if (NULL != sth) delete sth; } void Show(){cout << "(" << sth->a << ", " << sth->b << ")" << endl;} void setValue(int a, int b){sth->a = a; sth->b = b;} void getSthAddress() { cout << sth << endl; } }; int main(void) { CSomething sth(1, 2); CA ca(&sth); ca.Show(); CA cb(ca); // 调用缺省的隐式拷贝构造函数 cb.Show(); cb.setValue(2, 3); ca.Show(); cb.Show(); ca.getSthAddress(); cb.getSthAddress(); return 0; }
没有显示定义拷贝构造函数,其结果输出如下:
Call CSomething Constructor !
Call CA Constructor!
Call CSomething Constructor !
(1, 2)
(1, 2)
(2, 3)
(2, 3)
00396560
00396560
In the destructor of class CA...
Call the CSomething Destructor!
In the destructor of class CA...
Call the CSomething Destructor!
由输出结果可以看出:ca和cb的sth是同一个。
运行时会出错,其出错原因时程序结束时调用析构函数delete sth,由于ca.sth地址:00396560 和cb.sth地址:00396560相同,当ca.sth删除掉后,cb.sth再删除时就会出现问题。
下面是加上CA的拷贝构造函数:
CA( const CA & ob) { sth = new CSomething((ob.sth)->a,(ob.sth)->b); cout << "Call the Copy Constructor!" << endl; }
其输出为:
Call CSomething Constructor !
Call CA Constructor!
Call CSomething Constructor !
(1, 2)
Call CSomething Constructor !
Call the Copy Constructor!
(1, 2)
(1, 2)
(2, 3)
00396560
00396610
In the destructor of class CA...
Call the CSomething Destructor!
In the destructor of class CA...
Call the CSomething Destructor!
Call the CSomething Destructor!
由输出结果:
修改值后的ca.sth->a ca.sht->b
(1, 2)
修改值后的cb.sth->a cb.sht->b
(2, 3)
ca.sth:00396560
cb.sth00396610
可以看出达到了”深拷贝“的效果。
发表评论
-
Google编程风格
2012-04-01 17:05 1125Google编程风格(自己整 ... -
VS2008快捷键的设置
2012-02-02 13:43 2677VS2008快捷键的设置 VS2008默认的快捷键和VC++ ... -
LINK : fatal error LNK1000: Internal error during IncrBuildImage
2011-12-16 17:07 1239Win7安vc2008编译报LINK : fatal erro ... -
volatile关键字(摘自:百度百科)
2011-12-16 14:59 741volatile关键字[align=center][/alig ... -
InterlockedIncrement
2011-12-16 14:44 2202InterlockedIncrement[align=cent ... -
关键词explicit
2011-12-10 20:32 862关键词explicit[size=large][/size][ ... -
Souce Insight 设置
2011-12-09 17:16 2003Souce Insight 设置 【问题】 Source I ... -
VS2008下Boost库的安装编译下载boost库
2011-11-09 19:19 2076下载boost库 (最好去官网下,一般有SGI(GCC+用的较 ... -
C++多态技术的实现和反思(转)
2011-10-17 17:05 832面向对象技术最早出现于1960年代的Simula 67系统,并 ... -
memmove and memcpy
2011-07-31 13:11 1206memmove and memcpy 字符串的拷贝函数mem ... -
Polymorphism & Virtual Function
2011-07-25 21:38 874Polymorphism & Virtual Func ... -
C++ 不要重新定义继承的非虚函数
2011-07-18 14:15 1313不要重新定义继承的非虚函数 如果基类和派生类有相同的非虚函数 ... -
C++ 虚函数表解析
2011-07-14 21:02 1000C++ 虚函数表解析 为什么在C++机制里要有虚函数表?虚函 ... -
C++ 默认构造函数
2011-07-14 11:39 3748C++ 默认构造函数 一直 ... -
尽量使用const
2011-07-12 10:51 1120尽可能的使用const const: ... -
Operator=
2011-07-11 21:54 950Operator= 赋值构造函数 ... -
虚析构函数
2011-07-11 10:43 1185虚析构函数 一 、为何要单独讨论虚析构函数?虚函数在类中到底 ... -
初始化列表和声明顺序之间的关系
2011-07-11 09:18 1477初始化列表和声明的顺序之间的关系 类中数据成员的声明顺序和初 ... -
初始化函数列表和构造函数内赋值之区别
2011-07-10 17:05 4527初始化函数列表和构造 ... -
传值和传引用的区别
2011-07-10 15:30 2396传值和传引用的区别 在C语言中,大都是通过值传递,C++也是 ...
相关推荐
C++构造函数、析构函数和赋值操作符学习小结 C++ 构造函数、析构函数和赋值操作符是 C++ 编程语言中的基本组件,它们提供了对象的初始化、销毁和赋值操作。这些函数的正确性对整个类的正确性至关重要。 构造函数 ...
**析构函数**是与构造函数相对的,它在对象生命周期结束时,即对象销毁前被调用,用于清理对象可能占用的资源。虽然在提供的内容中没有直接提到析构函数,但在实际编程中,析构函数通常用来释放动态分配的内存或其他...
通过以上分析,我们可以看到`String`类的设计和实现涵盖了构造函数、析构函数以及多种操作符重载等功能,使得该类能够有效地处理字符串数据。对于学习C++语言的新手来说,这是一个很好的实践项目,可以帮助他们更好...
而当调用函数中有一个接受对象时,就将返回对象赋值给接收对象,这个返回对象在调用函数结束时调用析构函数。3. 当类有一个带有一个参数的构造函数时,可以用这个参数同类型的数据初始化这个对象,默认会调用这个...
在上面的代码中,我们定义了一个名为classA的类,其中包括一个默认构造函数、一个带参数的构造函数、一个复制构造函数、一个赋值操作符和一个析构函数。在main函数中,我们使用了多种方式来调用这些函数,例如语句1...
总之,构造函数、析构函数、拷贝构造函数、初始化列表、静态成员、常量、友元和运算符重载等是C++中构建复杂类结构的关键要素,理解并熟练掌握这些知识点对于编写高效、健壮的C++代码至关重要。
在上面的代码中,类 A 的拷贝构造函数和 operator=(赋值操作符重载)被声明为私有成员,以禁止外部用户对对象进行复制动作。 将构造函数和析构函数声明为私有或保护成员是一种常用的设计模式,可以用于实现 ...
我们都知道,在C++中建立一个类,这个类中肯定会包括构造函数、析构函数、复制构造函数和重载赋值操作;即使在你没有明确定义的情况下,编译器也会给你生成这样的四个函数。例如以下类: 代码如下: class CTest { ...
通过以上实现,我们可以看到构造函数、析构函数、拷贝构造函数和赋值操作符是管理动态内存的关键成员函数。它们不仅确保了内存的正确分配和释放,还保证了对象状态的一致性和安全性。对于开发人员来说,理解并正确...
初始化和赋值问题详解C++ 拷贝构造函数和赋值运算符详解C++中对构造函数和赋值运算符的复制和移动操作C++中复制构造函数和重载赋值操作符总结深入C++中构造函数、拷贝构造函数、赋值操作符、析构函数的调用过程总结...
根据给定的文件标题“C++构造函数和析构函数,拷贝,重载”以及描述“一道很好的习题代码,包含构造、析构、拷贝、重载”,我们可以了解到这段代码主要涉及到了C++中类的设计与实现,特别是构造函数、析构函数、拷贝...
本篇文章讲解了C++中类和对象的中级知识,涵盖了类的6个默认成员函数、构造函数、析构函数、拷贝构造函数、赋值运算符重载、日期类的实现等内容。 一、类的6个默认成员函数 在C++中,如果一个类中什么成员都没有,...
此外,还提供拷贝赋值操作符重载和析构函数来管理字符串的内存。 对象的创建可以使用new关键字动态分配内存,也可以在栈上直接声明。栈上的对象在声明周期结束时会自动调用析构函数,而动态分配的对象则需要程序员...
实现深拷贝通常需要在拷贝构造函数中手动复制这些资源,并在析构函数中相应地释放它们。这是因为编译器默认提供的浅拷贝构造函数并不知道如何处理这样的情况,因此程序员必须自己处理。 除了拷贝构造函数,课程内容...
实验三旨在深入理解C++中的类和对象的概念,特别是构造函数、析构函数、数据成员的访问控制、对象的赋值与复制、以及静态成员的相关知识。实验内容包括以下几个部分: 1. **类的定义与实现**: 首先,我们需要定义...
一个典型的String类实现,C++描述,里面包括String的构造函数,赋值构造函数,析构函数,赋值操作符的实现等
具体表现为两个或多个对象的指针成员变量指向相同的内存地址,当其中一个对象被销毁时,其析构函数会释放这块内存,而其他对象仍然持有指向这块已被释放内存的指针,从而导致指针挂起。 #### 3. 指针挂起的原因分析...