C++中一般创建对象,拷贝或赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法。下面就详细比较下三者之间的区别以及它们的具体实现

1.构造函数

构造函数是一种特殊的类成员函数,是当创建一个类的对象时,它被调用来对类的数据成员进行初始化和分配内存。(构造函数的命名必须和类名完全相同)

首先说一下一个C++的空类,编译器会加入哪些默认的成员函数

·默认构造函数和拷贝构造函数

·析构函数

·赋值函数(赋值运算符)

·取值函数

**即使程序没定义任何成员,编译器也会插入以上的函数! 

注意:构造函数可以被重载,可以多个,可以带参数;

析构函数只有一个,不能被重载,不带参数

 

而默认构造函数没有参数,它什么也不做。当没有重载无参构造函数时,

  A a就是通过默认构造函数来创建一个对象

下面代码为构造函数重载的实现

  1. class A
  2. {
  3. int m_i;
  4. Public:
  5. A()
  6. {
  7. Cout<<”无参构造函数”<<endl;
  8. }
  9. A(int i):m_i(i) {}  //初始化列表
  10. }</span>

2.拷贝构造函数

拷贝构造函数是C++独有的,它是一种特殊的构造函数,用基于同一类的一个对象构造和初始化另一个对象。

当没有重载拷贝构造函数时,通过默认拷贝构造函数来创建一个对象

A a;

A b(a);

A b=a;  都是拷贝构造函数来创建对象b

强调:这里b对象是不存在的,是用a 对象来构造和初始化b的!!

先说下什么时候拷贝构造函数会被调用:

在C++中,3种对象需要复制,此时拷贝构造函数会被调用

1)一个对象以值传递的方式传入函数体

2)一个对象以值传递的方式从函数返回

3)一个对象需要通过另一个对象进行初始化

 

什么时候编译器会生成默认的拷贝构造函数:

1)如果用户没有自定义拷贝构造函数,并且在代码中使用到了拷贝构造函数,编译器就会生成默认的拷贝构造函数。但如果用户定义了拷贝构造函数,编译器就不在生成。

2)如果用户定义了一个构造函数,但不是拷贝构造函数,而此时代码中又用到了拷贝构造函数,那编译器也会生成默认的拷贝构造函数。

 

因为系统提供的默认拷贝构造函数工作方式是内存拷贝,也就是浅拷贝。如果对象中用到了需要手动释放的对象,则会出现问题,这时就要手动重载拷贝构造函数,实现深拷贝。

下面说说深拷贝与浅拷贝:

浅拷贝:如果复制的对象中引用了一个外部内容(例如分配在堆上的数据),那么在复制这个对象的时候,让新旧两个对象指向同一个外部内容,就是浅拷贝。(指针虽然复制了,但所指向的空间内容并没有复制,而是由两个对象共用)

深拷贝:如果在复制这个对象的时候为新对象制作了外部对象的独立复制,就是深拷贝。

以上描述不详细,具体参见:http://www.cnblogs.com/liushui-sky/p/7728839.html

 

拷贝构造函数重载声明如下: 

A (const A&other)

下面为拷贝构造函数的实现:

  1. class A
  2. {
  3. int m_i
  4. A(const A& other):m_i(other.m_i)
  5. {
  6. Cout<<”拷贝构造函数”<<endl;
  7. }
  8. }</span>

 

3.赋值函数

当一个类的对象向该类的另一个对象赋值时,就会用到该类的赋值函数。

当没有重载赋值函数(赋值运算符)时,通过默认赋值函数来进行赋值操作

A a;

A b;

b=a; 

强调:这里a,b对象是已经存在的,是用a 对象来赋值给b的!!

 

赋值运算的重载声明如下:

 A& operator = (const A& other)

 

通常大家会对拷贝构造函数和赋值函数混淆,这儿仔细比较两者的区别:

1)拷贝构造函数是一个对象初始化一块内存区域,这块内存就是新对象的内存区,而赋值函数是对于一个已经被初始化的对象来进行赋值操作。

  1. class  A;
  2. A a;
  3. A b=a;   //调用拷贝构造函数(b不存在)
  4. A c(a) ;   //调用拷贝构造函数
  5. /****/
  6. class  A;
  7. A a;
  8. A b;
  9. b = a ;   //调用赋值函数(b存在)</span>

2)一般来说在数据成员包含指针对象的时候,需要考虑两种不同的处理需求:一种是复制指针对象,另一种是引用指针对象。拷贝构造函数大多数情况下是复制,而赋值函数是引用对象

3)实现不一样。拷贝构造函数首先是一个构造函数,它调用时候是通过参数的对象初始化产生一个对象。赋值函数则是把一个新的对象赋值给一个原有的对象,所以如果原来的对象中有内存分配要先把内存释放掉,而且还要检察一下两个对象是不是同一个对象,如果是,不做任何操作,直接返回。(这些要点会在下面的String实现代码中体现)

 

!!!如果不想写拷贝构造函数和赋值函数,又不允许别人使用编译器生成的缺省函数,最简单的办法是将拷贝构造函数和赋值函数声明为私有函数,不用编写代码。如:

  1. class A
  2. {
  3. private:
  4. A(const A& a); //私有拷贝构造函数
  5. A& operate=(const A& a); //私有赋值函数
  6. }</span>

如果程序这样写就会出错:

  1. A a;
  2. A b(a); //调用了私有拷贝构造函数,编译出错
  3. A b;
  4. b=a; //调用了私有赋值函数,编译出错</span>

所以如果类定义中有指针或引用变量或对象,为了避免潜在错误,最好重载拷贝构造函数和赋值函数。

 

下面以string类的实现为例,完整的写了普通构造函数,拷贝构造函数,赋值函数的实现。String类的基本实现见我另一篇博文。

 String::String(const char* str)    //普通构造函数

 {

  cout<<construct<<endl;

  if(str==NULL)        //如果str 为NULL,就存一个空字符串“”

 {
m_string=new char[];
*m_string ='\0';
} else { m_string= new char[strlen(str)+] ; //分配空间
strcpy(m_string,str); } } String::String(const String&other) //拷贝构造函数 {
cout<<"copy construct"<<endl;
m_string=new char[strlen(other.m_string)+]; //分配空间并拷贝
strcpy(m_string,other.m_string);
} String & String::operator=(const String& other) //赋值运算符
{
cout<<"operator =funtion"<<endl ;
if(this==&other) //如果对象和other是用一个对象,直接返回本身
{
return *this;
}
delete []m_string; //先释放原来的内存
m_string= new char[strlen(other.m_string)+];
strcpy(m_string,other.m_string);
return * this;
}</span>

一句话记住三者:

      对象不存在,且没用别的对象来初始化,就是调用了构造函数;

                对象不存在,且用别的对象来初始化,就是拷贝构造函数(上面说了三种用它的情况!)

                对象存在,用别的对象来给它赋值,就是赋值函数。

以上为本人结合很多资料和图书整理出来的,将核心的点都系统的理出来,全自己按条理写的,现在大家对普通构造函数,拷贝构造函数,赋值函数的区别和实现应该都清楚了。

转自:http://blog.csdn.net/zcyzsy/article/details/52132936

C++中构造函数,拷贝构造函数和赋值函数的区别和实现的更多相关文章

  1. 编写类String的构造函数、拷贝构造函数、析构函数和赋值函数

    一.题目: class String { public: String(const char *str = NULL); // 普通构造函数 String(const String &othe ...

  2. String的构造函数、析构函数和赋值函数

    编写类String的构造函数.析构函数和赋值函数 已知类String的原型为: class String { public: String(const char *str = NULL); // 普通 ...

  3. 编写类String 的构造函数、析构函数和赋值函数

    编写类String 的构造函数.析构函数和赋值函数,已知类String 的原型为:class String{public:String(const char *str = NULL); // 普通构造 ...

  4. C/C++面试题:编写类String的构造函数、析构函数和赋值函数。

    转https://www.cnblogs.com/alinh/p/9636500.html 考点:构造函数.析构函数和赋值函数的编写方法出现频率:☆☆☆☆☆已知类String的原型为:         ...

  5. 关于C++中的拷贝构造函数和赋值函数

    如果类定义的数据成员中存在指针或引用,那么最好重载这两个函数. 1.     定义 拷贝构造函数的定义格式:构造函数名(const 源类名& 引用对象形参名){} 赋值函数定义格式:源类名 & ...

  6. C++ 拷贝构造函数与赋值函数的区别(很严谨和全面)

    这里我们用类String 来介绍这两个函数: 拷贝构造函数是一种特殊构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用.当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式 ...

  7. C/C++中的拷贝构造函数和赋值构造函数

    代码: #include <iostream> #include <cstdio> using namespace std; class A{ public: A(){ cou ...

  8. C++中的拷贝构造函数

    一.拷贝构造函数: 格式: A(const  A& a);  总结: 系统为对象B分配了内存并完成了与对象testA的复制过程,就类对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制过 ...

  9. 编写类String的构造函数、析构函数和赋值函数

    已知类String的原型为: class String {   public:  String(const char *str = NULL); // 普通构造函数  String(const Str ...

随机推荐

  1. 【Unity】3.0 第3章 创建和导入3D模型

    分类:Unity.C#.VS2015 创建日期:2016-04-02 一.简介 利用Unity内置的基本模型和工具,不需要借助任何其他的三维建模软件,就可以直接创建出各种3D模型,这是这一章我们首先学 ...

  2. Linux 索引节点(inode)详解

    参考文章:http://www.ruanyifeng.com/blog/2011/12/inode.html

  3. 使用flow捕获js错误提高代码质量

    本文将介绍Flow及其主特性.下面分别从如何安装设置,如何添加类型注释,如何在运行时自动去掉注释等方面来介绍. 安装 目前Flow兼容的操作系统有Mac OS X,Linux(64位),Windows ...

  4. java.util.Date和java.sql.Date 一点区别

    最近无意中发现,在oracle中同一样的一个Date类型字段,存储的日期格式有两种不同的情况,第一种是2011-1-1 12:00:00,第二种是2011-1-1,仔细查找发现在向数据库中写数据的时候 ...

  5. 菜鸟学Java(十八)——异常

    每个学编程的人在编程的过程中都会遇到各种异常.那么当我们遇到异常的时候该怎么处理呢?针对不同的异常我们又该采取什么具体的处理方式呢?这些问题在我开始学编程的很长一段时间里我都不太清楚,还好随着不断的学 ...

  6. lua -- handler

    handler 将 Lua 对象及其方法包装为一个匿名函数. 格式: 函数 = handler(对象, 对象.方法) 在 quick-cocos2d-x 中,许多功能需要传入一个 Lua 函数做参数, ...

  7. .NET MVC结构框架下的微信扫码支付模式二 API接口开发测试

    直接上干货 ,我们的宗旨就是为人民服务.授人以鱼不如授人以渔.不吹毛求疵.不浮夸.不虚伪.不忽悠.一切都是为了社会共同进步,繁荣昌盛,小程序猿.大程序猿.老程序猿还是嫩程序猿,希望这个社会不要太急功近 ...

  8. (转)Go和HTTPS

    转自:http://studygolang.com/articles/2946 Go和HTTPS  2015-04-30   bigwhite  阅读 5688 次   4 人喜欢  3 条评论  收 ...

  9. java 多线程 28 : 多线程组件之 Semaphore 信号量

    Semaphore是非常有用的一个组件,它相当于是一个并发控制器,是用于管理信号量的.构造的时候传入可供管理的信号量的数值,这个数值就是控制并发数量的,就是同时能几个线程访问.我们需要控制并发的代码, ...

  10. Python3玩转单链表——逆转单向链表pythonic版

    [本文出自天外归云的博客园] 链表是由节点构成的,一个指针代表一个方向,如果一个构成链表的节点都只包含一个指针,那么这个链表就是单向链表. 单向链表中的节点不光有代表方向的指针变量,也有值变量.所以我 ...