构造函数是干什么的

该类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数,由构造函数完成成员的初始化工作,故:构造函数的作用:初始化对象的数据成员。

构造函数的种类

 1 class Complex
2 {
3
4 private :
5 double m_real;
6 double m_imag;
7
8 public:
9
10 // 无参数构造函数
11 // 如果创建一个类你没有写任何构造函数,则系统会自动生成默认的无参构造函数,函数为空,什么都不做
12 // 只要你写了一个下面的某一种构造函数,系统就不会再自动生成这样一个默认的构造函数,如果希望有一个这样的无参构造函数,则需要自己显示地写出来
13 Complex(void)
14 {
15 m_real = 0.0;
16 m_imag = 0.0;
17 }
18
19 // 一般构造函数(也称重载构造函数)
20 // 一般构造函数可以有各种参数形式,一个类可以有多个一般构造函数,前提是参数的个数或者类型不同(基于c++的重载函数原理)
21 // 例如:你还可以写一个 Complex( int num)的构造函数出来
22 // 创建对象时根据传入的参数不同调用不同的构造函数
23 Complex(double real, double imag)
24 {
25 m_real = real;
26 m_imag = imag;
27 }
28
29 // 复制构造函数(也称为拷贝构造函数)
30 // 复制构造函数参数为类对象本身的引用,用于根据一个已存在的对象复制出一个新的该类的对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中
31 // 若没有显示的写复制构造函数,则系统会默认创建一个复制构造函数,但当类中有指针成员时,由系统默认创建该复制构造函数会存在风险,具体原因请查询有关 “浅拷贝” 、“深拷贝”的文章论述
32 Complex(const Complex & c)
33 {
34 // 将对象c中的数据成员值复制过来
35 m_real = c.m_real;
36 m_img = c.m_img;
37 }
38
39 // 类型转换构造函数,根据一个指定的类型的对象创建一个本类的对象
40 // 例如:下面将根据一个double类型的对象创建了一个Complex对象
41 Complex::Complex(double r)
42 {
43 m_real = r;
44 m_imag = 0.0;
45 }
46
47 // 等号运算符重载
48 // 注意,这个类似复制构造函数,将=右边的本类对象的值复制给等号左边的对象,它不属于构造函数,等号左右两边的对象必须已经被创建
49 // 若没有显示的写=运算符重载,则系统也会创建一个默认的=运算符重载,只做一些基本的拷贝工作
50 Complex &operator=(const Complex &rhs)
51 {
52 // 首先检测等号右边的是否就是左边的对象本,若是本对象本身,则直接返回
53 if ( this == &rhs )
54 {
55 return *this;
56 }
57
58 // 复制等号右边的成员到左边的对象中
59 this->m_real = rhs.m_real;
60 this->m_imag = rhs.m_imag;
61
62 // 把等号左边的对象再次传出
63 // 目的是为了支持连等 eg: a=b=c 系统首先运行 b=c
64 // 然后运行 a= ( b=c的返回值,这里应该是复制c值后的b对象)
65 return *this;
66 }
67 };

下面使用上面定义的类对象来说明各个构造函数的用法:

 1 void main()
2 {
3 // 调用了无参构造函数,数据成员初值被赋为0.0
4 Complex c1,c2;
5
6 // 调用一般构造函数,数据成员初值被赋为指定值
7 Complex c3(1.0,2.5);
8 // 也可以使用下面的形式
9 Complex c3 = Complex(1.0,2.5);
10
11 // 把c3的数据成员的值赋值给c1
12 // 由于c1已经事先被创建,故此处不会调用任何构造函数
13 // 只会调用 = 号运算符重载函数
14 c1 = c3;
15
16 // 调用类型转换构造函数
17 // 系统首先调用类型转换构造函数,将5.2创建为一个本类的临时对象,然后调用等号运算符重载,将该临时对象赋值给c1
18 c2 = 5.2;
19
20 // 调用拷贝构造函数( 有下面两种调用方式)
21 Complex c5(c2);
22 Complex c4 = c2; // 注意和 = 运算符重载区分,这里等号左边的对象不是事先已经创建,故需要调用拷贝构造函数,参数为c2
23
24 }

参考:http://www.cnblogs.com/xkfz007/archive/2012/05/11/2496447.html

复制构造函数

几个原则:

C++ primer p406 :复制构造函数是一种特殊的构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显示使用复制构造函数。当该类型的对象传递给函数或从函数返回该类型的对象时,将隐式调用复制构造函数。

C++支持两种初始化形式:复制初始化(int a = 5;)和直接初始化(int a(5);)对于其他类型没有什么区别,对于类类型直接初始化直接调用实参匹配的构造函数,复制初始化总是调用复制构造函数,也就是说:

A x(2);  //直接初始化,调用构造函数
A y = x;  //复制初始化,调用复制构造函数

必须定义复制构造函数的情况:

只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显式地定义复制构造函数也可以复制;有的类有一个数据成员是指针,或者是有成员表示在构造函数中分配的其他资源,这两种情况下都必须定义复制构造函数。

什么情况使用复制构造函数:

类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:
(1)一个对象以值传递的方式传入函数体 
(2)一个对象以值传递的方式从函数返回 
(3)一个对象需要通过另外一个对象进行初始化。

深拷贝和浅拷贝:

所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。在“深拷贝”的情况下,对于对象中动态成员,就不能仅仅简单地赋值了,而应该重新动态分配空间

如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝

上面提到,如果没有自定义复制构造函数,则系统会创建默认的复制构造函数,但系统创建的默认复制构造函数只会执行“浅拷贝”,即将被拷贝对象的数据成员的值一一赋值给新创建的对象,若该类的数据成员中有指针成员,则会使得新的对象的指针所指向的地址与被拷贝对象的指针所指向的地址相同,delete该指针时则会导致两次重复delete而出错。下面是示例:

 1 #include <iostream.h>
2 #include <string.h>
3 class Person
4 {
5 public :
6
7 // 构造函数
8 Person(char * pN)
9 {
10 cout << "一般构造函数被调用 !\n";
11 m_pName = new char[strlen(pN) + 1];
12 //在堆中开辟一个内存块存放pN所指的字符串
13 if(m_pName != NULL)
14 {
15 //如果m_pName不是空指针,则把形参指针pN所指的字符串复制给它
16 strcpy(m_pName ,pN);
17 }
18 }
19
20 // 系统创建的默认复制构造函数,只做位模式拷贝
21 Person(Person & p)
22 {
23 //使两个字符串指针指向同一地址位置
24 m_pName = p.m_pName;
25 }
26
27 ~Person( )
28 {
29 delete m_pName;
30 }
31
32 private :
33 char * m_pName;
34 };
35
36 void main( )
37 {
38 Person man("lujun");
39 Person woman(man);
40
41 // 结果导致 man 和 woman 的指针都指向了同一个地址
42
43 // 函数结束析构时
44 // 同一个地址被delete两次
45 }
46
47
48 // 下面自己设计复制构造函数,实现“深拷贝”,即不让指针指向同一地址,而是重新申请一块内存给新的对象的指针数据成员
49 Person(Person & chs);
50 {
51 // 用运算符new为新对象的指针数据成员分配空间
52 m_pName=new char[strlen(p.m_pName)+ 1];
53
54 if(m_pName)
55 {
56 // 复制内容
57 strcpy(m_pName ,chs.m_pName);
58 }
59
60 // 则新创建的对象的m_pName与原对象chs的m_pName不再指向同一地址了
61 }

重载赋值操作符:

通过定义operate=的函数,可以对赋值进行定义。像其他任何函数一样,操作符函数有一个返回值和形参表。形参表必须具有与该操作符操作数书目相同的形参(如果操作符是一个成员,则包括隐式this形参)。赋值是二元运算,所以该操作符函数有两个形参:第一个形参(隐含的this指针)对应着左操作数,第二个形参对应右操作数。

一个应用了对赋值号重载的拷贝构造函数的例子:

 1 #include <iostream>
2
3 using namespace std;
4
5 class A
6 {
7 public:
8 A(int);//构造函数
9 A(const A &);//拷贝构造函数
10 ~A();
11 void print();
12 int *point;
13 A &operator=(const A &);
14 };
15
16 A::A(int p)
17 {
18 point = new int;
19 *point = p;
20 }
21
22 A::A(const A &b)
23 {
24 *this = b;
25 cout<<"调用拷贝构造函数"<<endl;
26 }
27
28 A::~A()
29 {
30 delete point;
31 }
32
33 void A::print()
34 {
35 cout<<"Address:"<<point<<" value:"<<*point<<endl;
36 }
37
38 A &A::operator=(const A &b)
39 {
40 if( this != &b)
41 {
42 delete point;
43 point = new int;
44 *point = *b.point;
45 }
46 }
47
48
49 int main()
50 {
51 A x(2);
52 A y = x;
53 x.print();
54 delete x.point;
55 y.print();
56
57 return 0;
58 }

C++构造函数详解(复制构造函数 也是 拷贝构造函数)的更多相关文章

  1. C++构造函数详解(复制构造函数)

    构造函数是干什么的 该类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数,由构造函数完成成员的初始化工作,故:构造函数的作用:初始化对象的数据成员. 构造函数的种类 class Compl ...

  2. C++复制(拷贝)构造函数详解

    本文转载自 http://blog.csdn.net/lwbeyond/article/details/6202256,在此感谢作者 CTemp B(A);       //复制构造函数,C++风格的 ...

  3. C++拷贝(复制)构造函数详解

    原文:http://blog.csdn.net/lwbeyond/article/details/6202256/[侵删] 一. 什么是拷贝构造函数 首先对于普通类型的对象来说,它们之间的复制是很简单 ...

  4. [转]c++类的构造函数详解

    c++构造函数的知识在各种c++教材上已有介绍,不过初学者往往不太注意观察和总结其中各种构造函数的特点和用法,故在此我根据自己的c++编程经验总结了一下c++中各种构造函数的特点,并附上例子,希望对初 ...

  5. C++构造函数详解及显式调用构造函数

    来源:http://www.cnblogs.com/xkfz007/archive/2012/05/11/2496447.html       c++类的构造函数详解                  ...

  6. C++中构造函数详解及显式调用构造函数

    C++构造函数详解及显式调用构造函数                                         c++类的构造函数详解                        一. 构造函 ...

  7. c++构造函数详解

    c++构造函数的知识在各种c++教材上已有介绍,不过初学者往往不太注意观察和总结其中各种构造函数的特点和用法,故在此我根据自己的c++编程经验总结了一下c++中各种构造函数的特点,并附上例子,希望对初 ...

  8. c++类的构造函数详解

    c++类的构造函数详解 一. 构造函数是干什么的 class Counter{ public:         // 类Counter的构造函数         // 特点:以类名作为函数名,无返回类 ...

  9. c++构造函数详解(转)

    c++构造函数的知识在各种c++教材上已有介绍,不过初学者往往不太注意观察和总结其中各种构造函数的特点和用法,故在此我根据自己的c++编程经验总结了一下c++中各种构造函数的特点,并附上例子,希望对初 ...

  10. 转 C++拷贝构造函数详解

    C++拷贝构造函数详解 一. 什么是拷贝构造函数 首先对于普通类型的对象来说,它们之间的复制是很简单的,例如: int a = 100; int b = a; 而类对象与普通对象不同,类对象内部结构一 ...

随机推荐

  1. 【python】How to change the Jupyter start-up folder

    Copy the Jupyter Notebook launcher from the menu to the desktop. Right click on the new launcher and ...

  2. 翻译:A Tutorial on the Device Tree (Zynq) -- Part I

    A Tutorial on the Device Tree (Zynq) -- Part I 此教程的目的 本教程是针对Xilinx' Zynq-7000 EPP设备(一个集成了FPGA的ARM Co ...

  3. 2016/06/13 phpexcel 未完待续

    ①准备工作: 1,php版本不能太低 2,去官网下载PHPExcel插件    http://phpexcel.codeplex.com/ 3,解压后提取classes文件夹到工作目录,并重命名为PH ...

  4. HBase协处理器同步二级索引到Solr(续)

    一. 已知的问题和不足二.解决思路三.代码3.1 读取config文件内容3.2 封装SolrServer的获取方式3.3 编写提交数据到Solr的代码3.4 拦截HBase的Put和Delete操作 ...

  5. CrateDb

    CrateDB: Real-time SQL Database for Machine Data & IoT | Crate.io https://crate.io/

  6. Cache 简介

    一.什么是缓存1.Cache是高速缓冲存储器 一种特殊的存储器子系统,其中复制了频繁使用的数据以利于快速访问2.凡是位于速度相差较大的两种硬件/软件之间的,用于协调两者数据传输速度差异的结构,均可称之 ...

  7. ossfs常见配置错误

    以下问题出现在非root用户下 执行echo ××××> /etc/passwd-ossfs  bash: /etc/passwd-ossfs: Permission denied 使用sudo ...

  8. 织梦栏目页分页title加"第N页"

    以上就是调用栏目管理的SEO标题代码:{dede:field.seotitle /}的方法,这样充分体现列表页标题显示的效果,有利于搜索引擎的收录. 第二个问题就是这样列表页的标题都成了一个样子的了, ...

  9. js程序开发-1

    <h1>数组的常用操作</h1> push() 方法可向数组的末尾添加一个或多个元素,并返回新数组的长度. unshift() 方法可向数组的开头添加一个或更多元素,并返回新数 ...

  10. JQuery验证成功之后,使用ajax提交数据

    function checkForm(){ validator = $("#commentForm").validate({// #formId为需要进行验证的表单ID error ...