C++构造函数注意事项
1、匿名对象
首先应该明确匿名对象,匿名对象是之没有对象名,调用完构造函数后即析构的对象。下面通过代码捕捉类的构造函数和析构函数,以进行说明:
#include <iostream>
using namespace std; class Solution{
public:
Solution(int a, int b):m_num1(a), m_num2(b) {
cout << "有参构造函数的调用" << endl;
};
Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){
cout << "拷贝构造函数的调用" << endl;
}
~Solution(){
cout << "析构函数的调用" << endl;
}
private:
int m_num1;
int m_num2;
}; int main()
{
Solution(8,9); // Solution(8,9) 匿名对象 system("pause");
return 0;
}
代码运行结果为:

通过代码运行结果可以看到,创建匿名对象的时候,调用了类的构造函数,随后立即调用了析构函数。我们可以直接利用匿名对象进行初始化类的成员的初始化,代码如下:
#include <iostream>
using namespace std; class Solution{
public:
Solution(int a, int b):m_num1(a), m_num2(b) {
cout << "有参构造函数的调用" << endl;
};
Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){
cout << "拷贝构造函数的调用" << endl;
}
~Solution(){
cout << "析构函数的调用" << endl;
} int m_num1;
int m_num2;
}; int main()
{
Solution s1(Solution(8,9));
// Solution s1 = Solution(8,9); //显式 Solution(8,9) 匿名对象初始化类成员
// Solution s1 = {8,9}; //{8,9}等价于Solution(8,9)
cout << "s1.m_num1 = " << s1.m_num1 << endl;
cout << "s1.m_num2 = " << s1.m_num2 << endl;
system("pause");
return 0;
}
运行结果如下:

代码调用了一次构造函数,可见匿名对象可以初始化类成员,就是将匿名对象转化成了s1对象,这里需要与拷贝构造函数区分开:
#include <iostream>
using namespace std; class Solution{
public:
Solution(int a, int b):m_num1(a), m_num2(b) {
cout << "有参构造函数的调用" << endl;
};
Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){
cout << "拷贝构造函数的调用" << endl;
}
~Solution(){
cout << "析构函数的调用" << endl;
} int m_num1;
int m_num2;
}; int main()
{
Solution s1(10,11);
Solution s2(s1);
cout << "s2.m_num1 = " << s2.m_num1 << endl;
cout << "s2.m_num2 = " << s2.m_num2 << endl;
system("pause");
return 0;
}
代码运行结果为:

对比以上两个代码可以发现,通过匿名对象初始化类成员并不是拷贝构造,只是一种替换,通过匿名对象初始化类成员并不会调用拷贝构造函数。
2、拷贝构造函数的调用时机
今年秋招笔试题最爱考查构造函数的调用时机,通常会结合继承和多态来考察,这里先说明一下拷贝构造函数的调用时机,后面再详细说明带继承和多态的构造函数调用时机。
一、使用一个已经创建的对象来初始化一个新对象,如上面的代码,创建对象s1的时候调用了有参构造函数,通过s1来初始化s2的时候调用了拷贝构造。
二、函数的参数是需要值传递的对象的时候
三、函数返回对象的时候
下面通过代码验证,当函数的参数是一个需要值传递的对象的情况:
#include <iostream>
using namespace std; class Solution{
public:
Solution(int a, int b):m_num1(a), m_num2(b) {
cout << "有参构造函数的调用" << endl;
};
Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){
cout << "拷贝构造函数的调用" << endl;
}
~Solution(){
cout << "析构函数的调用" << endl;
}
public:
int m_num1;
int m_num2;
}; void showClassNum(Solution s) {
cout << s.m_num1 << endl;
cout << s.m_num2 << endl;
} int main()
{
Solution s1(10,11);
showClassNum(s1);
system("pause");
return 0;
}
代码运行结果为:

通过代码运行结果可以看到,s1对象调用了有参构造函数,当把s1传给函数的参数s的时候调用了拷贝构造函数,函数执行完调用了s对象的析构函数。
当函数的返回值是一个对象的时候,也会调用拷贝构造函数:
#include <iostream>
using namespace std; class Solution{
public:
Solution(int a, int b):m_num1(a), m_num2(b) {
cout << "有参构造函数的调用" << endl;
};
Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){
cout << "拷贝构造函数的调用" << endl;
}
~Solution(){
cout << "析构函数的调用" << endl;
} void changeNum(int a, int b) {
m_num1 = a;
m_num2 = b;
} void showNum() {
cout << "m_num1 = " << m_num1 << endl;
cout << "m_num2 = " << m_num2 << endl;
}
public:
int m_num1;
int m_num2;
}; Solution clearClassNum(Solution s) {
s.changeNum(0,0);
return s;
} int main()
{
Solution s1 (10,10);
s1 = clearClassNum (s1);
s1.showNum();
system("pause");
return 0;
}
代码运行结果为:

从代码运行结果可以看出来,函数传参的时候调用了拷贝构造,然后函数返回一个对象的时候的也调用了拷贝构造。
需要注意的是,函数要返回对象的时候,不要使用引用的方式返回,因为函数的内的变量存放在堆栈区,在函数执行完毕后就会释放这块内存,引用就会出现问题。
3、深拷贝和浅拷贝
面试的时候比较喜欢问的问题,首先浅拷贝就是我们常用的拷贝,实现了对象成员的拷贝,深拷贝就是在堆区申请空间,然后再进行拷贝操作,浅拷贝可以由编译器完成,但是深拷贝需要我们自己完成,就是有在堆区开辟的内存,就一定要自己提供拷贝构造函数,防止浅拷贝带来的重复内存释放问题。代码验证如下:
#include <iostream>
using namespace std; class Solution{
public:
Solution(int a, int b):m_num1(a), pm_num2(new int(b)) {
cout << "有参构造函数的调用" << endl;
};
//如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题,导致程序出错
Solution(const Solution& s):m_num1(s.m_num1), pm_num2(new int(*s.pm_num2)) {
cout << "拷贝构造函数的调用" << endl;
}
~Solution(){
cout << "析构函数的调用,释放堆区申请的内存" << endl;
if (pm_num2 != nullptr) {
delete pm_num2;
}
} void changeNum(int a, int b) {
m_num1 = a;
*pm_num2 = b;
} void showNum() {
cout << "m_num1 = " << m_num1 << endl;
cout << "m_num2 = " << *pm_num2 << endl;
}
public:
int m_num1;
int* pm_num2;
}; void func()
{
Solution s1 (10,10);
Solution s2(s1);
s2.showNum();
} int main()
{ func();
system("pause");
return 0;
}
代码运行结果为:
C++构造函数注意事项的更多相关文章
- php 父类子类构造函数注意事项
网上流传的2点: PHP的构造函数继承必须满足以下条件: 当父类有构造函数的声明时,子类也必须有声明,否则会出错. 在执行父类的构造函数时,必须在子类中引用parent关键字. 第1点不需要. 第二个 ...
- Java学习笔记之:Java构造函数
一.引言 构造函数是一种特殊的函数.其主要功能是用来在创建对象时初始化对象, 即为v对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中.构造函数与类名相同,可重载多个不同的构造函数. 构 ...
- JavaScript构造函数详解
构造函数就是初始化一个实例对象,对象的prototype属性是继承一个实例对象. 构造函数注意事项: 1.默认函数首字母大写 2.构造函数并没有显示返回任何东西.new 操作符会自动创建给定的类型并返 ...
- C++类中函数(构造函数、析构函数、拷贝构造函数、赋值构造函数)
[1]为什么空类可以创建对象呢? 示例代码如下: #include <iostream> using namespace std; class Empty { }; void main() ...
- OC基础--构造方法
OC语言中类的构造方法学了两种: 一.方法一:[类名 new] 例:[Person new] 缺点:可扩展性不强,假如在Person类中有_age 成员变量,在初始化时想让_age 中的值为20,ne ...
- PHP基础入门(五)---PHP面向对象
前言: 今天来和大家介绍一下PHP的面向对象.说到面向对象,我不得不提一下面向过程,因为本人在初学时,常常分不清楚. 那么面向对象和面向过程有什么区别呢?下面给大家简单介绍一下: 面向对象专注于由哪个 ...
- 【PHP】PHP面向对象编程--phpOOP入门
PHP从入门到精通 之PHP的面相对象编程 面向对象编程(Object Oriented Programming, OOP, 面向对象程序设计)是一种计算机编程架构,OOP的一条基本原则是计算机程序 ...
- ES6中的类
前面的话 大多数面向对象的编程语言都支持类和类继承的特性,而JS却不支持这些特性,只能通过其他方法定义并关联多个相似的对象,这种状态一直延续到了ES5.由于类似的库层出不穷,最终还是在ECMAScri ...
- PHP基础入门(五)---PHP面向对象实用基础知识
前言: 今天来和大家介绍一下PHP的面向对象.说到面向对象,我不得不提一下面向过程,因为本人在初学时,常常分不清楚面向对象和面向过程,下面就来给大家介绍一下它们的区别: 面向对象专注于由哪个对象来处理 ...
随机推荐
- excel中if函数的用法
IF函数有三个参数,语法如下: =IF(条件判断, 结果为真返回值, 结果为假返回值) 第一参数是条件判断,比如说"A1="百度""或"21>3 ...
- K8s Master当作Node使用的方法
1.使用下面的命令操作使得master 可以作为node使用 承载pod kubectl taint nodes --all node-role.kubernetes.io/master- 可能会出现 ...
- JSON,XML设计模式详解
JSON在Java中的应用: Json概念: json 是一种轻量级的数据交换格式,采用完全独立于编程语言的文本格式用来存储和表示数据.JSON的语言简洁清晰,广为大众所欢迎,是一种理想的数据交换语言 ...
- 学习PHP中的国际化日期格式化操作
对于国际化功能来说,日期相关的格式化操作也是一块重头戏,毕竟不同的时区,不同的国家对于日期的表示方式都会有些不同.今天我们主要来学习的就是国际化地表示日期相关的信息内容. 日期格式化 首先就是最直接的 ...
- requests接口自动化-assert断言
断言,自动判断接口返回的结果与预期结果是否一致 from common.get_mysql import * def test_assert(): a=0 b=1 # assert a,'断言失败打印 ...
- CI框架 core
https://blog.csdn.net/admin_admin/article/details/51769805 1.扩展控制器 1.在application/core新建一个自己的控制器(MY_ ...
- python3.7+flask+alipay 支付宝付款功能
文档参考github:https://github.com/fzlee/alipay/blob/master/docs/init.md 沙箱环境配置:https://opendocs.alipay.c ...
- requests + 正则表达式 获取 ‘猫眼电影top100’。
使用 进程池Pool 提高爬取数据的速度. 1 # !/usr/bin/python 2 # -*- coding:utf-8 -*- 3 import requests 4 from request ...
- WPF实现截图(仿微信截图)
WPF开发者QQ群: 340500857 | 微信群 -> 进入公众号主页 加入组织 每日一笑 肚子疼,去厕所排便,结果什么都没拉出来.看着自己坐在马桶上痛苦又努力却一无所获的样子,仿佛看到了 ...
- 一个故事,一段代码告诉你如何使用不同语言(Golang&C#)提供相同的能力基于Consul做服务注册与发现
目录 引言 什么是微服务 传统服务 微服务 什么是服务注册与服务发现 为什么要使用不同的语言提供相同的服务能力 服务协调器 服务注册 Golang C#(.NetCore3.1) 服务发现 通过Htt ...