在C++中,调用拷贝构造函数有三种情况:

1.一个对象作为函数参数,以值传递的方式传入函数体

2.一个对象作为函数返回值,以值传递的方式从函数返回

3.一个对象用于给另外一个对象进行初始化(复制初始化).

拷贝构造函数必须以引用的形式传递(参数为引用值).其原因如下:

当一个对象以传递值的方式传一个函数的时候,拷贝构造函数自动的调用来生成函数中的对象.

这样会导致无限循环地调用拷贝构造函数,直至栈溢出.

以前,一直有个误解,以为以同类型的对象调用"="时,就会调用赋值符.参看以下的例子:

 1 class CTest {
2 public:
3 CTest();
4 CTest(const CTest&);
5 CTest& operator=(const CTest &);
6 };
7 CTest::CTest()
8 {
9 cout<<"Constructor of CTest"<<endl;
10 }
11 CTest::CTest(const CTest& arg)
12 {
13 cout<<"Copy Constructor of CTest"<<endl;
14 }
15 CTest& CTest::operator=(const CTest& arg)
16 {
17 cout<<"Assign function of CTest"<<endl;
18 }
19 int main()
20 {
21 CTest a;
22 CTest b(a);
23 CTest c = a;
24 a = c;
25 return 0;
26 }

按照以前的理解,第21~24行代码,应该分别调用构造函数,拷贝构造函数,赋值符函数,赋值符函数.

然而最终如下,不是如自己所想...说明以前的理解是错误的.

Constructor of CTest
Copy Constructor of CTest
Copy Constructor of CTest
Assign function of CTest

第23行代码调用的是拷贝构造函数,不是赋值符函数,但第24行代码调用的赋值符函数,不是拷贝构造函数.原因如下:

拷贝构造函数创建新的对象,而赋值符函数不创建新对象,它要求"="的左右对象均已存在,它的作用就是把"="右边的对象的值赋给左边的对象.

虽然编译器会提供拷贝构造函数和赋值符函数,但是有时候编译器提供的这些函数,并不能满足我们的需求,因而需要自定义拷贝构造函数和赋值函数.

这里就会引出一个新问题,什么时候需要自定义拷贝构造函数和赋值符函数.

简单的规则:如果需要定义一个非空的析构函数,那么,通常情况下也需要定义一个拷贝构造函数和赋值符函数.

通常的原则是:

1.对于凡是包含动态分配成员或包含指针成员的类都应该提供拷贝构造函数;

2.在提供拷贝构造函数的同时,还应该考虑重载"="赋值操作符,即提供赋值符函数.

当我们知道需要自定义拷贝构造函数和赋值符函数时,就得考虑如何良好的实现它们.

当自定义copying函数(包含拷贝构造函数和赋值符函数)时,需要确保以下两点:

1.复制所有的local成员变量

2.调用所有base classes内的适当的copying函数,完成基类的copying.

下面是一个具体的例子:

 1 void logcall(const std::string& funcName);    //制造一个log entry
2 class Customer {
3 public:
4 ...
5 Customer(const Customer& rhs);
6 Customer& operator=(const Customer& rhs);
7 ...
8 private:
9 std::string name;
10 };
11 Customer::Customer(const Customer& rhs):name(rhs.name)
12 {
13 logCall("Customer copy constructor");
14 }
15 Customer& Customer::operator=(const Customer& rhs)
16 {
17 logCall("Customer copy assignment operator");
18 name = rhs.name; //疑惑,为什么在copying函数里可以通过对象调用私有变量?
19 return *this;
20 }
21
22 class PriorityCustomer:public Customer {
23 public:
24 ...
25 PriorityCustomer(const PriorityCustomer& rhs);
26 PriorityCustomer& operator=(const PriorityCustomer& rhs);
27 ...
28 private:
29 int priority;
30 };
31 PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs):Customer(rhs),priority(rhs.priority)
32 {
33 logCall("PriorityCustomer copy constructor");
34 }
35 PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer& rhs)
36 {
37 logCall("PriorityCustomer copy assignment operator");
38 Customer::operator=(rhs); //对base class成分进行赋值动作
39 priority = rhs.priority;
40 return *this;
41 }

第18行代码中,通过对象调用私有变量,似乎违背了私有变量的含义,有点无法理解,具体的分析和理解,请参考:

http://www.cnblogs.com/dwdxdy/archive/2012/07/17/2595741.html

使用opertator=函数给对象赋值时,若右边的对象和调用对象相同,即自我赋值,会引发自我赋值的不安全问题.具体分析和解决方案,请参考:

http://www.cnblogs.com/dwdxdy/archive/2012/07/17/2595821.html

参考资料:http://baike.baidu.com/view/1266959.htm

【C++】拷贝构造函数和赋值符函数的更多相关文章

  1. C++中的构造函数,拷贝构造函数和赋值运算

    关于C++中的构造函数,拷贝构造函数和赋值运算,以前看过一篇<高质量C++/C编程指南>的文章中介绍的很清楚,网上能搜索到,如果想详细了解这方面的知识可以参看一下这篇文章. 常见的给对象赋 ...

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

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

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

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

  4. C++雾中风景6:拷贝构造函数与赋值函数

    在进行C++类编写的过程之中,通常会涉及到类的拷贝构造函数与类的赋值函数.初涉类编写的代码,对于两类函数的用法一直是挺让人困惑的内容.这篇文章我们会详细来梳理拷贝构造函数与赋值函数的区别. 1.调用了 ...

  5. C++中:默认构造函数、析构函数、拷贝构造函数和赋值函数——转

    对于一个空类,编译器默认产生4个成员函数:默认构造函数.析构函数.拷贝构造函数和赋值函数.1.构造函数:构造函数是一种特殊的类成员,是当创建一个类的时候,它被调用来对类的数据成员进行初始化和分配内存. ...

  6. CPP_类默认函数:构造函数,拷贝构造函数,赋值函数和析构函数

    类默认函数:构造函数,拷贝构造函数,赋值函数和析构函数 // person.h #ifndef _PERSON_H_ #define _PERSON_H_ class Person{ public : ...

  7. C++中构造函数,拷贝构造函数和赋值函数的区别和实现

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

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

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

  9. C++ 拷贝构造函数和赋值构造函数

    转自:http://blog.chinaunix.net/uid-28662931-id-3496326.html 一.拷贝构造函数 int main(int argc, char * argv[]) ...

随机推荐

  1. springboot---->集成mybatis开发(一)

    这里面我们介绍一下springboot与mybatis的集成,主要完成了mybatis的真分页.一个成熟的人往往发觉可以责怪的人越来越少,人人都有他的难处. springboot简单集成mytbati ...

  2. Ibatis.Net 动态SQL语句学习(六)

    在学习动态SQL语句之前,首先必须对条件查询有一定了解,先来学习如何向IBatis.Net的映射文件里传入参数吧. 一.条件查询 1.传递单个参数 如根据Id查询: <select id=&qu ...

  3. Java 基本语法---Java方法

    Java 基本语法---Java方法 0.概述 方法:就是用来解决一类问题的代码的有序组合,是一个功能模块: 在方法部分,只使用对象名词调用方法: Scanner sc = new Scanner(S ...

  4. 【linux】shell中命令替换$(cmd)和符号`cmd`

    来源:https://zhidao.baidu.com/question/485498670.html 作用: $(cmd)和`cmd`的作用是相同的,在执行一条命令时,会先将其中的 ``,或者是$( ...

  5. 【AtCoder】ARC084

    C - Snuke Festival 对于每个B二分求出几个A比它小记为sum 然后对于每个C就是比它小的B的sum的和 #include <bits/stdc++.h> #define ...

  6. 【LOJ】#2126. 「HAOI2015」数组游戏

    题解 简单分析一下就知道\(\lfloor \frac{N}{i} \rfloor\)相同的\(i\)的\(sg\)函数相同 所以我们只要算\(\sqrt{n}\)个\(sg\)函数就好 算每一个\( ...

  7. 【LOJ】#2071. 「JSOI2016」最佳团体

    题解 01分数规划,二分加树背包-- 代码 #include <bits/stdc++.h> #define enter putchar('\n') #define space putch ...

  8. R语言编程艺术(5)R语言编程进阶

    本文对应<R语言编程艺术> 第14章:性能提升:速度和内存: 第15章:R与其他语言的接口: 第16章:R语言并行计算 ================================== ...

  9. AngularJs指令配置参数scope详解

    AngularJs最重要也是最难理解的模块之一就是它的指令(directive)了,自定义指令配置有很多个参数,下面我只说说其中scope的配置极其含义. scope表示指令的作用域,它有三个可选值: ...

  10. [转]Splay算法

    首先声明,本教程的对象是完全没有接触过splay的OIer,大牛请右上角. 先看一道题目: skydec有n个数,每次他都会把一些数放进一些盒子里,由于skydec太傻×,所以他不能判断数的大小,现在 ...