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. 机器学习——EM算法

    1 数学基础 在实际中,最小化的函数有几个极值,所以最优化算法得出的极值不确实是否为全局的极值,对于一些特殊的函数,凸函数与凹函数,任何局部极值也是全局极致,因此如果目标函数是凸的或凹的,那么优化算法 ...

  2. python对象引用和垃圾回收

    变量="标签" 变量a和变量b引用同一个列表: >>> a = [1, 2, 3] >>> b = a >>> a.appen ...

  3. [转载]SELinux 环境下网络服务设置,配置 Apache 、Samba、NFS

    原文地址:http://www.ibm.com/developerworks/cn/linux/l-cn-selinux-services1/index.html?ca=drs- 引言 SELinux ...

  4. P3480-[POI2009]KAM-Pebbles【阶梯博弈】

    正题 题目链接:https://www.luogu.com.cn/problem/P3480 题目大意 \(n\)个石头堆上进行\(\text{Nim}\)游戏,不过需要满足每次操作前后都有\(a_i ...

  5. CF802O-April Fools‘ Problem(hard)【wqs二分,优先队列】

    正题 题目链接:https://www.luogu.com.cn/problem/CF802O 题目大意 \(n\)天每条有\(a_i\)和\(b_i\). 每条可以花费\(a_i\)准备至多一道题, ...

  6. CRM是什么,你有认真了解过CRM吗?

    这是CRM的一个简单定义 客户关系管理 (CRM)是一种用于管理公司与客户和潜在客户的所有关系和互动的技术.目标很简单:改善业务关系.CRM 系统可帮助公司与客户保持联系.简化流程并提高盈利能力. 当 ...

  7. Vue router中携带参数与获取参数

    Vue router中携带参数与获取参数 携带参数 query方式,就是?+&结构,例如/login?id=1 <router-link :to="{ name:'login' ...

  8. 8.3 MHA 搭建

    操作步骤 1.配置主从 1.1 master /etc/my.cnf server-id log-bin skip-name-resolve 1.2 master 建立repl用户 grant rep ...

  9. 如何基于Jupyter notebook搭建Spark集群开发环境

    摘要:本文介绍如何基于Jupyter notebook搭建Spark集群开发环境. 本文分享自华为云社区<基于Jupyter Notebook 搭建Spark集群开发环境>,作者:apr鹏 ...

  10. JVM学习笔记——GC算法

    GC 算法 GC 即 Garbage Collection 垃圾回收.JVM 中的 GC 99%发生在堆中,而 Java 堆中采用的垃圾回收机制为分代收集算法.即将堆分为新生代和老年代,根据不同的区域 ...