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

                                        c++类的构造函数详解                        

一、 构造函数是干什么的

 1 class Counter
2 {
3
4 public:
5 // 类Counter的构造函数
6 // 特点:以类名作为函数名,无返回类型
7 Counter()
8 {
9 m_value = 0;
10 }
11
12 private:
13
14 // 数据成员
15 int m_value;
16 }

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

eg:    Counter c1;
        编译系统为对象c1的每个数据成员(m_value)分配内存空间,并调用构造函数Counter( )自动地初始化对象c1的m_value值设置为0

故:

        构造函数的作用:初始化对象的数据成员。

二、 构造函数的种类

 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
68 };

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

 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
25
26 }

三、思考与测验

1. 仔细观察复制构造函数

      Complex(const Complex & c)
{
// 将对象c中的数据成员值复制过来
m_real = c.m_real;
m_img = c.m_img;
}

为什么函数中可以直接访问对象c的私有成员?

2. 挑战题,了解引用与传值的区别

 1  Complex test1(const Complex& c)
2 {
3 return c;
4 }
5
6 Complex test2(const Complex c)
7 {
8 return c;
9 }
10
11 Complex test3()
12 {
13 static Complex c(1.0,5.0);
14 return c;
15 }
16
17 Complex& test4()
18 {
19 static Complex c(1.0,5.0);
20 return c;
21 }
22
23 void main()
24 {
25 Complex a,b;
26
27 // 下面函数执行过程中各会调用几次构造函数,调用的是什么构造函数?
28
29 test1(a);
30 test2(a);
31
32 b = test3();
33 b = test4();
34
35 test2(1.2);
36 // 下面这条语句会出错吗?
37 test1(1.2); //test1( Complex(1.2 )) 呢?
38 }

四、附录(浅拷贝与深拷贝)

       上面提到,如果没有自定义复制构造函数,则系统会创建默认的复制构造函数,但系统创建的默认复制构造函数只会执行“浅拷贝”,即将被拷贝对象的数据成员的值一一赋值给新创建的对象,若该类的数据成员中有指针成员,则会使得新的对象的指针所指向的地址与被拷贝对象的指针所指向的地址相同,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
34 char * m_pName;
35 };
36
37 void main( )
38 {
39 Person man("lujun");
40 Person woman(man);
41
42 // 结果导致 man 和 woman 的指针都指向了同一个地址
43
44 // 函数结束析构时
45 // 同一个地址被delete两次
46 }
47
48
49 // 下面自己设计复制构造函数,实现“深拷贝”,即不让指针指向同一地址,而是重新申请一块内存给新的对象的指针数据成员
50 Person(Person & chs);
51 {
52 // 用运算符new为新对象的指针数据成员分配空间
53 m_pName=new char[strlen(p.m_pName)+ 1];
54
55 if(m_pName)
56 {
57 // 复制内容
58 strcpy(m_pName ,chs.m_pName);
59 }
60
61 // 则新创建的对象的m_pName与原对象chs的m_pName不再指向同一地址了
62 }

C++ 各种构造函数的更多相关文章

  1. .NET 基础 一步步 一幕幕[面向对象之构造函数、析构函数]

    构造函数.析构函数 构造函数: 语法: //无参的构造函数 [访问修饰符] 函数名() :函数名必须与类名相同. //有参的构造函数 [访问修饰符] 函数名(参数列表):函数名必须与类名相同. 作用: ...

  2. javascript工厂模式和构造函数模式创建对象

    一.工厂模式 工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程(本书后面还将讨论其他设计模式及其在JavaScript 中的实现).考虑到在ECMAScript 中无法创 ...

  3. JS继承之借用构造函数继承和组合继承

    根据少一点套路,多一点真诚这个原则,继续学习. 借用构造函数继承 在解决原型中包含引用类型值所带来问题的过程中,开发人员开始使用一种叫做借用构造函数(constructor stealing)的技术( ...

  4. PHP与JAVA构造函数的区别

    早期的PHP是没有面向对象功能的,但是随着PHP发展,从PHP4开始,也加入了面向对象.PHP的面向对象语法是从JAVA演化而来,很多地方类似,但是又发展出自己的特色.以构造函数来说,PHP4中与类同 ...

  5. C++ 拷贝构造函数和赋值运算符

    本文主要介绍了拷贝构造函数和赋值运算符的区别,以及在什么时候调用拷贝构造函数.什么情况下调用赋值运算符.最后,简单的分析了下深拷贝和浅拷贝的问题. 拷贝构造函数和赋值运算符 在默认情况下(用户没有定义 ...

  6. golang语言构造函数

    1.构造函数定义 构造函数 ,是一种特殊的方法.主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中.特别的一个类可以有多个构造函数 ,可根据其参数个 ...

  7. 前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型

    前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型 前言(题外话): 有人说拖延症是一个绝症,哎呀治不好了.先不说这是一个每个人都多多少少会有的,也不管它究竟对生活有多么大的 ...

  8. Aop动态生成代理类时支持带参数构造函数

    一.背景 在某些情况下,我们需要植入AOP代码的类并没有默认构造函数.那么此时动态生成的代理类也需要相同签名的构造函数,并且内部调用原始类的构造函数.自己折腾了1晚上没搞定,现在搞定了发出来供大家一起 ...

  9. C#的泛型的类型参数可以有带参数的构造函数的约束方式吗?

    Review后看到标题让我十分羞愧自己语文功底太差,估计...请见谅......我还特地把这句写回开头了...... 问题 前天遇到的一个问题,所以在MSDN发了个问,刚也丰富了下问题,关于泛型的. ...

  10. Android中自定义样式与View的构造函数中的第三个参数defStyle的意义

    零.序 一.自定义Style 二.在XML中为属性声明属性值 1. 在layout中定义属性 2. 设置Style 3. 通过Theme指定 三.在运行时获取属性值 1. View的第三个构造函数的第 ...

随机推荐

  1. tcpdump软件使用

    tcpdump是一个抓包工具, -w 选项是把抓到的包写到二进制文件中,一般扩展名是.cap或.dmp,但tcpdump程序创建文件时并不添加扩展名,可自己指定. -i 是指定要抓包的interfac ...

  2. Python自动化测试面试题-经验篇

    目录 Python自动化测试面试题-经验篇 Python自动化测试面试题-用例设计篇 Python自动化测试面试题-Linux篇 Python自动化测试面试题-MySQL篇 Python自动化测试面试 ...

  3. Vue 2升级 Vue 3初探小细节

    前言 嗯,偶尔看看学习Vue 3技能啦,此前用过Vue 2做过一点东西,Vue 3已面世一段时间,于是乎,我来看看所遇到的问题是否在Vue 3中得到解决,首先,我们来讲讲一个例子在Vue 2中的实现, ...

  4. endnote x9.3.3 for windows安装教程

    EndNote X9.3.3 是一款非常nice的实用型文献管理软件,EndNote X9功能极其强劲,便捷好用.本文提供EndNote X9.3.3安装破解激活教程.方法,内附EndNote x9. ...

  5. xmind2020 zen 10.2.1win/mac/linux安装教程

    xmind是一款优秀的思维导图软件,本文教大家如何安装xmind zen 2020 10.2.1版本,解锁使用全部功能,包括去掉xmind zen水印.上传图片等功能,支持windows/mac/li ...

  6. c++ 父类 子类 虚表占用内存空间情况

    #include <iostream> using namespace std; class C {}; class A:public C { private: long a; long ...

  7. Table类

    Interpreter类, class Interpreter: public CC_INTERP_ONLY(CppInterpreter) NOT_CC_INTERP(TemplateInterpr ...

  8. 第一章 Vant的引入和基本使用

    Vant UI组件库基本使用 一.Vant官网 https://youzan.github.io/vant/#/zh-CN/ 二.安装Vant npm install --save vant 三.安装 ...

  9. 初探Docker CentOS 7.9 2009 Mini 操作系统环境初始化 和Docker初始化

    初探docker 什么是docker?docker就是一种虚拟化技术,将一个服务虚拟化成一个拥有操作系统内核作为基石的快速使用服务.不用担心环境不同服务效果 不同. docker 官网可以从中央仓库中 ...

  10. selenium WebDriverWait

    Selenium WebDriverWait的知识: 一.webdrivewait 示例代码  from selenium import webdriver  from selenium.webdri ...