c++中的类之构造函数
一、构造函数的缘由
本文我们主要来讲解c++中类的构造函数,其中涉及了深拷贝和浅拷贝的问题,这也是在面试笔试中经常会碰到的问题。如果您是第一次听说构造函数,可能会觉得这个名字有点高大上,而它却和实际中的工程问题有关。在正式的讲解前,我们先来思考一个问题:(注意,本文采用的课件来源于狄泰软件学院,感谢狄泰的唐老师!)

#include <stdio.h> class Test
{
private:
int i;
int j;
public:
int getI() { return i; }
int getJ() { return j; }
}; Test gt; // 全局变量放在bss段 int main()
{
// 访问全局变量gt的的成员变量值
printf("gt.i = %d\n", gt.getI());
printf("gt.j = %d\n", gt.getJ()); Test t1;
// 访问局部变量t1的的成员变量值
printf("t1.i = %d\n", t1.getI());
printf("t1.j = %d\n", t1.getJ()); Test* pt = new Test;
// 访问堆空间pt的的成员变量值
printf("pt->i = %d\n", pt->getI());
printf("pt->j = %d\n", pt->getJ()); delete pt; // 注意释放堆空间 return ;
}
成员变量的初始值
通过这个简单的例子,我们发现,同样是声明一个类的对象,因为对象所在的存储空间不同(bss段、堆空间、栈),导致其对象的成员变量的初始值不相同。对于我们类的使用者来说,我们当然不希望出现这种情况。于是,我们希望在定义一个类的对象的同时,初始化其成员变量的值,统一化,不管是在bss段,堆空间、栈上。

因此,我们可以提供下面的解决方案:

这样的方式虽然可以解决我们之前遇到的问题,但是在实际的使用过程中会觉得不好用。

为此,c++的设计者想出了一个办法,即构造函数:

注意,构造函数必须满足: 1. 与类的名字相同 2.无返回值
二、有参构造函数和重载构造函数


注意这里的初始化方式,即“ ()“” 和“ = ”。

在创建一个对象数组的时候,我们可以手工调用构造函数来初始化该对象数组。
#include <stdio.h> class Test
{
private:
int m_value;
public:
Test()
{
printf("Test()\n"); m_value = ;
}
Test(int v)
{
printf("Test(int v), v = %d\n", v); m_value = v;
}
int getValue()
{
return m_value;
}
}; int main()
{
Test ta[] = {Test(), Test(), Test()}; // 手工调用构造函数 for(int i=; i<; i++)
{
printf("ta[%d].getValue() = %d\n", i , ta[i].getValue());
} // int i(100); // 这样的方式是初始化i
Test t = Test(); // 初始化方式 printf("t.getValue() = %d\n", t.getValue()); return ;
}
创建对象数组
三、无参构造函数和拷贝构造函数
下面来介绍两个特殊的构造函数:


注意:没有构造函数是指连拷贝构造函数都没有。
3.2 深拷贝和浅拷贝

浅拷贝只是简单的成员变量复制。我们可以看下面的例子:
#include <stdio.h> class Test
{
private:
int i;
int j;
int* p;
public:
int getI()
{
return i;
}
int getJ()
{
return j;
}
int* getP()
{
return p;
}
Test(const Test& t)
{
i = t.i;
j = t.j; /* 这里是浅拷贝,p和t.p所指向的内存空间相同
*/
// p = t.p ; /* 注意这里是深拷贝,与浅拷贝不同的是,重新在堆空间申请内存空间,
* 并将该空间的值和t.p的相同,这样就可以使得对象的逻辑状态相同
*/
p = new int;
*p = *t.p;
}
Test(int v)
{
i = ;
j = ;
p = new int;
*p = v;
}
void free()
{
delete p;
}
}; int main()
{
Test t1();
Test t2(t1); printf("t1.i = %d, t1.j = %d, *t1.p = %d\n", t1.getI(), t1.getJ(), *t1.getP());
printf("t2.i = %d, t2.j = %d, *t2.p = %d\n", t2.getI(), t2.getJ(), *t2.getP()); t1.free(); // 释放两次,如果是浅拷贝会出问题,而深拷贝不会
t2.free(); return ;
}
深拷贝和浅拷贝

既然我们已经了解了深拷贝和浅拷贝的区别,那么什么时候我们该使用深拷贝呢?
(即申请堆空间、打开文件、网络端口等操作)

四、初始化列表
在正式讲解初始化列表前,我们先来思考一个问题:


实际上,我们会发现这个类并没有给成员变量ci一个初始值,所以会出错。那么我们怎么样为const 成员变量初始化一个值呢?这里就引入了初始化列表的概念。如下:

可以看到初始化列表是在构造函数函数名之后加上一个冒号,之间用逗号隔开,m1、m2、m3是代表类的成员变量,括号里面的是对应的初始值。需要注意的是:

#include <stdio.h> class Value
{
private:
int mi;
public:
Value(int i)
{
printf("i = %d\n", i);
mi = i;
}
int getI()
{
return mi;
}
}; class Test
{
private:
// 成员变量的初始化顺序和成员变量的声明顺序有关,而与初始化列表中的位置无关
Value m2;
Value m3;
Value m1;
public:
Test() : m1(), m2(), m3()
{
//由于初始化列表先于构造函数执行,所以这一句代码最后执行
printf("Test::Test()\n");
}
}; int main()
{
Test t; return ;
}
成员变量的初始化顺序
我们再回到之前的那个问题:

一个小问题:

(注意,本文采用的课件来源于狄泰软件学院,感谢狄泰的唐老师!)
c++中的类之构造函数的更多相关文章
- OOP3(继承中的类作用域/构造函数与拷贝控制/继承与容器)
当存在继承关系时,派生类的作用域嵌套在其基类的作用域之内.如果一个名字在派生类的作用域内无法正确解析,则编译器将继续在外层的基类作用域中寻找该名字的定义 在编译时进行名字查找: 一个对象.引用或指针的 ...
- c++中的类(构造函数,析构函数的执行顺序)
类对象的初始化顺序 新对象的生成经历初始化阶段(初始化列表显式或者隐式的完成<这部分有点像java里面的初始化块>)——> 构造函数体赋值两个阶段 1,类对象初始化的顺序(对于没有父 ...
- 关于c++11中static类对象构造函数线程安全的验证
在c++11中,static静态类对象在执行构造函数进行初始化的过程是线程安全的,有了这个特征,我们可以自己动手轻松的实现单例类,关于如何实现线程安全的单例类,请查看c++:自己动手实现线程安全的c+ ...
- TypeScript完全解读(26课时)_8.ES6精讲-ES6中的类(进阶)
8.TypeScript完全解读-ES6精讲-类(进阶) 在index.ts内引入 Food创建的实例赋值给Vegetabled这个原型对象,这样使用Vegetables创建实例的时候,就能继承到Fo ...
- scala中的面向对象定义类,构造函数,继承
我们知道scala中一切皆为对象,函数也是对象,数字也是对象,它是一个比java还要面向对象的语言. 定义scala的简单类 class Point (val x:Int, val y:Int) 上面 ...
- Qt中新建类构造函数的初始化参数列表
使用Qt-creator自动生成一个窗体应用程序时会自动创建一个新的类,我的程序中名为MyDialog,类的定义为: #ifndef MYDIALOG_H #define MYDIALOG_H #in ...
- scala入门教程:scala中的面向对象定义类,构造函数,继承
我们知道scala中一切皆为对象,函数也是对象,数字也是对象,它是一个比java还要面向对象的语言. 定义scala的简单类 class Point (val x:Int, val y:Int) 上面 ...
- C#中派生类调用基类构造函数用法分析
这里的默认构造函数是指在没有编写构造函数的情况下系统默认的无参构造函数 1.当基类中没有自己编写构造函数时,派生类默认的调用基类的默认构造函数例如: ? 1 2 3 4 5 6 7 8 9 10 11 ...
- C++-什么时候需要在类的构造函数中使用初始化列表
1,如果基类没有default构造函数,则意味着其不能自己初始化.如果其被派生,派生类的构造函数要负责调用基类的构造函数,并传递给它需要的参数.下例中Base 2,如果类成员没有默认构造函数.下例中E ...
随机推荐
- Centos搭建开发环境,PHP7+ Nginx1.12+ Mysql5.7
1.更新yum源 yum -y update 2. 安装 epel-release yum install epel-release -y 检测安装成功:yum search nginx 结果含有: ...
- JavaScript:方法&对象大全
方法 方法的原型链 <html> <head> <title></title> </head> <script type=" ...
- 1-3 hibernate核心对象关系映射 xxx.hbm.xml
详见 http://www.cnblogs.com/biehongli/p/6532800.html 1 <?xml version="1.0" encoding='utf ...
- CentOS7安装Postgresql
执行命令 Yum install postgresql-server Yum install postgresql-contrib 安装完成后,检查postgresql的服务状态 Systemctl ...
- python全栈开发-Day2 布尔、流程控制、循环
python全栈开发-Day2 布尔 流程控制 循环 一.布尔 1.概述 #布尔值,一个True一个False #计算机俗称电脑,即我们编写程序让计算机运行时,应该是让计算机无限接近人脑,或者说人 ...
- elementUI源码修改定制
1.修改elementUI源码 首先从Git上克隆代码或者下载代码包 进入文件夹打开终端或Git Bash Here,运行npm install 安装依赖包.npm run dev 打开网页http: ...
- i/10和i取最后两位的精妙算法(前方高能)
i/10; q2 = (i2 * 52429) >>> (16+3); 52429/524288 = 0.10000038146972656, 524288 = 1 << ...
- 【django之stark组件】
一.需求 仿照django的admin,开发自己的stark组件.实现类似数据库客户端的功能,对数据进行增删改查. 二.实现 1.在settings配置中分别注册这三个app # Applicatio ...
- 关于使用Mybatis的使用说明(一)【未完善待更新】
(一)搭建Mybatis环境 (1)先导入常用的jar包:并且需要将lib文件夹下的包导入到项目中 (2)创建config文件夹,配置log4j.properties文件 # Global loggi ...
- C程序设计-----第1次作业
一. PTA作业. 在完成PTA作业的时候我没有认真读题.每次都是提交完整代码 6-1(1) #include <stdio.h> //P++等价于(p)++还是等价于*(p++)? ...