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++构造函数注意事项的更多相关文章

  1. php 父类子类构造函数注意事项

    网上流传的2点: PHP的构造函数继承必须满足以下条件: 当父类有构造函数的声明时,子类也必须有声明,否则会出错. 在执行父类的构造函数时,必须在子类中引用parent关键字. 第1点不需要. 第二个 ...

  2. Java学习笔记之:Java构造函数

    一.引言 构造函数是一种特殊的函数.其主要功能是用来在创建对象时初始化对象, 即为v对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中.构造函数与类名相同,可重载多个不同的构造函数. 构 ...

  3. JavaScript构造函数详解

    构造函数就是初始化一个实例对象,对象的prototype属性是继承一个实例对象. 构造函数注意事项: 1.默认函数首字母大写 2.构造函数并没有显示返回任何东西.new 操作符会自动创建给定的类型并返 ...

  4. C++类中函数(构造函数、析构函数、拷贝构造函数、赋值构造函数)

    [1]为什么空类可以创建对象呢? 示例代码如下: #include <iostream> using namespace std; class Empty { }; void main() ...

  5. OC基础--构造方法

    OC语言中类的构造方法学了两种: 一.方法一:[类名 new] 例:[Person new] 缺点:可扩展性不强,假如在Person类中有_age 成员变量,在初始化时想让_age 中的值为20,ne ...

  6. PHP基础入门(五)---PHP面向对象

    前言: 今天来和大家介绍一下PHP的面向对象.说到面向对象,我不得不提一下面向过程,因为本人在初学时,常常分不清楚. 那么面向对象和面向过程有什么区别呢?下面给大家简单介绍一下: 面向对象专注于由哪个 ...

  7. 【PHP】PHP面向对象编程--phpOOP入门

     PHP从入门到精通 之PHP的面相对象编程 面向对象编程(Object Oriented Programming, OOP, 面向对象程序设计)是一种计算机编程架构,OOP的一条基本原则是计算机程序 ...

  8. ES6中的类

    前面的话 大多数面向对象的编程语言都支持类和类继承的特性,而JS却不支持这些特性,只能通过其他方法定义并关联多个相似的对象,这种状态一直延续到了ES5.由于类似的库层出不穷,最终还是在ECMAScri ...

  9. PHP基础入门(五)---PHP面向对象实用基础知识

    前言: 今天来和大家介绍一下PHP的面向对象.说到面向对象,我不得不提一下面向过程,因为本人在初学时,常常分不清楚面向对象和面向过程,下面就来给大家介绍一下它们的区别: 面向对象专注于由哪个对象来处理 ...

随机推荐

  1. Java学习笔记--面对对象OOP

    面向对象编程 OOP 面向对象&面向过程 面向对象编程的本质:以类的方式组织代码,以对象的方法组织数据 面向对象编程的三大特征: 封装 继承 多态 方法 静态方法 通过 static 关键词说 ...

  2. electron-vue 开发问题合集

    (一)Found 'electron' but not as a devDependency, pruning anyway 原因:对electron没有严格要求的话可以忽略,不影响打包,但会影响第三 ...

  3. 【原创】自制string类型(已完成)

    这篇文章首发于360doc http://www.360doc.com/content/21/0526/17/73755266_979099504.shtml ,其实360doc里面的那个也是我的帐号 ...

  4. 记一次docker compose的低级错误

    记一次docker compose的低级错误 问题 ​ 今天在学习dockercompose的时候,启动docker compose up,结果却出现异常 Error response from da ...

  5. Java基础系列(27)- 什么是方法

    何谓方法 System.out.println();它是什么呢 # System:类 # out:对象 # println():方法 Java方法是语句的集合,它们在一起执行一个功能 方法是解决一类问 ...

  6. hadoop 学习笔记二

    NameNode的持久化(persistent)(day4,1) 类似于:Redis redis中的持久化文件是相互独立的当两个持久化文件同时存在时默认使用的是aof ,但是namenode 的持久化 ...

  7. linux,apache,php,mysql常用的查看版本信息的方法

    1. 查看linux的内核版本,系统信息,常用的有三种办法:   uname -a:   more /etc/issue;    cat /proc/version; 2. 查看apache的版本信息 ...

  8. java 工具类 验证码

    第一步: 引入工具类 工具类一: import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import jav ...

  9. flask_sqlalchemy 查询结果转dict 终极解决方案

    之前为了学习Python,试着拿Flask作框架搞小网站,感觉还不错,基本就抛弃了PHP.前段时间做了一个微信小程序,想着yii框架拿来写几十个小接口是不是浪费了,就继续用flask写api了,哪想到 ...

  10. windows kubectl 远程操作k8s

    在windows 电脑上配置kubectl远程操作kubernetes 一.下载windows版的kubectl可执行文件 下载地址 二.创建.kube 建议使用git bash cd ~ mkdir ...