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++ 各种构造函数的更多相关文章
- .NET 基础 一步步 一幕幕[面向对象之构造函数、析构函数]
构造函数.析构函数 构造函数: 语法: //无参的构造函数 [访问修饰符] 函数名() :函数名必须与类名相同. //有参的构造函数 [访问修饰符] 函数名(参数列表):函数名必须与类名相同. 作用: ...
- javascript工厂模式和构造函数模式创建对象
一.工厂模式 工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程(本书后面还将讨论其他设计模式及其在JavaScript 中的实现).考虑到在ECMAScript 中无法创 ...
- JS继承之借用构造函数继承和组合继承
根据少一点套路,多一点真诚这个原则,继续学习. 借用构造函数继承 在解决原型中包含引用类型值所带来问题的过程中,开发人员开始使用一种叫做借用构造函数(constructor stealing)的技术( ...
- PHP与JAVA构造函数的区别
早期的PHP是没有面向对象功能的,但是随着PHP发展,从PHP4开始,也加入了面向对象.PHP的面向对象语法是从JAVA演化而来,很多地方类似,但是又发展出自己的特色.以构造函数来说,PHP4中与类同 ...
- C++ 拷贝构造函数和赋值运算符
本文主要介绍了拷贝构造函数和赋值运算符的区别,以及在什么时候调用拷贝构造函数.什么情况下调用赋值运算符.最后,简单的分析了下深拷贝和浅拷贝的问题. 拷贝构造函数和赋值运算符 在默认情况下(用户没有定义 ...
- golang语言构造函数
1.构造函数定义 构造函数 ,是一种特殊的方法.主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中.特别的一个类可以有多个构造函数 ,可根据其参数个 ...
- 前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型
前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型 前言(题外话): 有人说拖延症是一个绝症,哎呀治不好了.先不说这是一个每个人都多多少少会有的,也不管它究竟对生活有多么大的 ...
- Aop动态生成代理类时支持带参数构造函数
一.背景 在某些情况下,我们需要植入AOP代码的类并没有默认构造函数.那么此时动态生成的代理类也需要相同签名的构造函数,并且内部调用原始类的构造函数.自己折腾了1晚上没搞定,现在搞定了发出来供大家一起 ...
- C#的泛型的类型参数可以有带参数的构造函数的约束方式吗?
Review后看到标题让我十分羞愧自己语文功底太差,估计...请见谅......我还特地把这句写回开头了...... 问题 前天遇到的一个问题,所以在MSDN发了个问,刚也丰富了下问题,关于泛型的. ...
- Android中自定义样式与View的构造函数中的第三个参数defStyle的意义
零.序 一.自定义Style 二.在XML中为属性声明属性值 1. 在layout中定义属性 2. 设置Style 3. 通过Theme指定 三.在运行时获取属性值 1. View的第三个构造函数的第 ...
随机推荐
- 成功解决1406, “Data too long for column ‘txt‘ at row 1“
这是因为数据库里该字段的数据类型所给的数据空间太小.MySQL将截断超过指定列宽度的任何插入值.为了让这个不报错,可以尝试切换MySQL模式不使用严格模式. SET @@global.sql_mode ...
- 前端性能优化实践-gzip
一名优秀的前端工程师必备技能之一就是要会性能监控,并且能相应的进行性能优化.最近,有需求将项目做一些优化,提升用户的体验.看了一下项目并没有开启gzip,于是着手实现gzip压缩,下面就是具体的实践过 ...
- 【学习笔记】Tensor多维数组和axis的理解
Tensor多维数组和axis的理解 今天在编写程序的时候一直对于axis=0或等于1搞不明白,这样对于整个numpy或者是tensorflow的基本运算和数据处理都会很模糊,所以花了一些时间来搞清楚 ...
- sql-4-函数
常见函数 1.运算函数 select ABS(-8) -- 绝对值 select ceiling(9.4) -- 向上取整 select floor(9.4) -- 向下取整 select rand( ...
- Leetcode:230. 二叉搜索树中第K小的元素
Leetcode:230. 二叉搜索树中第K小的元素 Leetcode:230. 二叉搜索树中第K小的元素 思路: 利用BST的中序历遍的结果为其排序后的结果,我们可以利用其特性直接找到第k个中序遍历 ...
- odoo中接口开发
文章参考:https://blog.csdn.net/qq_33472765/article/details/81913627案例0000001接口调用请求说明:https请求方式:GET(请使用ht ...
- Python基础之tabview
以前写过界面,但是没有记录下来,以至于现在得从头学习一次,论做好笔记的重要性. 现在学习的是怎么写一个tabview出来,也就是用tkinter做一个界面切换的效果.参考链接:https://blog ...
- CC2B本地环境搭建步骤及部署问题解决
由于最近的项目是之前没接触过的netbeans+glassfish,记录一下最近在工作中搭建本地环境的步骤及遇到的一些问题解决方法: 1.配置java jdk 此过程中遇到一个问题就是在配置系统环境变 ...
- intouch制作历史报警查询(时间查询,筛选关键字)
在项目中,intouch制作历史报警查询已属于标配功能,如何做出按时间以及关键字来进行综合查询,提高历史报警查询效率仍然是一个值得研究的问题,接下来参考网上文章自己总结下如何制作. 1.DTPicke ...
- mysql 占用90%多的CPU,解决思路
网站打开很慢,爆出了连接数据库的错误,进入服务器,top 看了下,mysql占用cpu 基本维持在90以上: mysql> show variables like '%slow%'; ...