C++拷贝构造函数的调用时机
一、拷贝构造函数调用的时机
当以拷贝的方式初始化对象时会调用拷贝构造函数,这里需要注意两个关键点,分别是以拷贝的方式
和初始化对象
1. 初始化对象
初始化对象是指,为对象分配内存后第一次向内存中填充数据,这个过程会调用构造函数,对象被创建后必须立即初始化。也就是说只要创建对象就会调用构造函数。
2.初始化和赋值的区别
初始化和赋值都是将数据写入内存中,从表面看,初始化在很多时候都是以复制的方式来实现的,很容易引起混淆。在定义的同时进行复制叫做初始化,定义完成以后再赋值(不管定义的时候有没有赋值)就叫做赋值。初始化只能由一次,赋值可以由很多次。具体可以看下面的示例。
int a = 100; //以赋值的方式初始化
a = 200; //赋值
a = 300; //赋值
int b; //默认初始化
b = 29; //赋值
b = 39; //赋值
对于基本的数据类型,我们很少会区分初始化和赋值,即使是混淆了,也不会出现什么错误。但是对于类,他们的区别就非常重要了,因为初始化时会调用构造函数(以拷贝的方式初始化时会调用拷贝构造函数),而赋值时会调用重载过的赋值运算符。
二、拷贝构造函数和普通构造函数调用的例子
#include<iostream>
#include<string>
using namespace std;
class Student {
public:
Student(string name = "", int age = 0); //普通构造函数
Student(const Student &Stu); //拷贝构造函数
Student& operator=(const Student &Stu); // 重载 = 运算符
void display();
private:
string m_name;
int m_age;
};
//普通构造函数
Student::Student(string name , int age )
{
m_name = name;
m_age = age;
}
//拷贝构造函数
Student::Student(const Student &Stu)
{
this->m_name = Stu.m_name;
this->m_age = Stu.m_age;
cout << "Copy constructor was called." << endl;
}
// 重载 = 运算符
Student& Student:: operator=(const Student &Stu)
{
this->m_name = Stu.m_name;
this->m_age = Stu.m_age;
cout << "operator=() was called." << endl;
return *this;
}
void Student::display()
{
cout << m_age << " " << m_age << endl;
}
int main()
{
Student stu1("Xiao Ming", 18); // 调用普通构造函数
Student stu2("Xiao Wang", 18); // 调用普通构造函数
Student stu3 = stu1; // 调用拷贝构造函数
stu3 = stu2; //调用operator=()
Student stu4(stu1); // 调用拷贝构造函数
Student stu5; // 调用普通构造函数
stu5 = stu2; //调用operator=()
return 0;
}
/*
输出:
Copy constructor was called.
operator=() was called.
Copy constructor was called.
operator=() was called.
*/
三、以拷贝的方式初始化对象
1. 初始化对象时会调用构造函数,不同的初始化方式会调用不同的构造函数:
- 如果用传递进来的实参初始化对象,那么会调用普通的构造函数。
- 如果用现有对象的数据来初始化对象,就会调用拷贝构造函数,这就是以拷贝的方式初始化对象。
2. 以拷贝的方式来初始化对象的几种情况:
将其它对象作为实参。
Student stu1("Xiao Ming", 18); // 普通初始化
Student stu4(stu1); // 以拷贝的方式进行初始化
/* 即使我们不在类中显示定义拷贝构造函数,这种初始化方式也是有效的,编译器会生成默认的拷贝构造函数 */
在创建对象的同时赋值。
Student stu1("Xiao Ming", 18); // 普通初始化
Student stu3 = stu1; // 以拷贝的方式进行初始化
/* 这是最常见的一种以拷贝的方式初始化对象的情况 */
函数的形参为类类型。
如果函数的形参为类类型(对象),那么调用函数时要将另外一个对象作为实参传递进来赋值给形参,这也是以拷贝的方式初始化形参对象,如下所示。
void func(Student s){
//TODO:
}
Student stu1("Xiao Ming", 18); // 普通初始化
func(stu1); //以拷贝的方式初始化 /* func() 函数有一个 Student 类型的形参 s,将实参 stu 传递给形参 s 就是以拷贝的方式初始化的过程。 */
函数返回值为类类型(与编译器有关不绝对)
当函数的返回值为类类型时,return 语句会返回一个对象,不过为了防止局部对象被销毁,也为了防止通过返回值修改原来的局部对象,编译器并不会直接返回这个对象,而是根据这个对象先创建出一个临时对象(匿名对象),再将这个临时对象返回。而创建临时对象的过程,就是以拷贝的方式进行的,会调用拷贝构造函数,如下所示。
Student func(){
Student stu1("Xiao Ming", 18); // 普通初始化
return stu1;
}
Student stu = func();
C++拷贝构造函数的调用时机的更多相关文章
- 【校招面试 之 C/C++】第4题 拷贝构造函数被调用的3个时机
1.被调用的3个时机: (1)直接初始化或拷贝初始化: (2)将一个对象作为一个实参传递,形参采用非指针或非引用的对象进行接收时(指针即指向了同一块空间,并未实现拷贝:而引用就是实参本身): (3)函 ...
- C++拷贝构造函数被调用的时机
拷贝构造函数调用的几种情况: 当用类的一个对象去初始化该类的另一个对象(或引用)时系统自动调用拷贝构造函数实现拷贝赋值. 若函数的形参为类对象,调用函数时,实参赋值给形参,系统自动调用拷贝构造函数.( ...
- 【转】C++的拷贝构造函数深度解读,值得一看
建议看原帖 地址:http://blog.csdn.net/lwbeyond/article/details/6202256 一. 什么是拷贝构造函数 首先对于普通类型的对象来说,它们之间的复制是很 ...
- C++拷贝构造函数详解(转载)
一. 什么是拷贝构造函数 首先对于普通类型的对象来说,它们之间的复制是很简单的,例如: int a = 100; int b = a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员 ...
- 【转】 c++拷贝构造函数(深拷贝,浅拷贝)详解
c++拷贝构造函数(深拷贝,浅拷贝)详解 2013-11-05 20:30:29 分类: C/C++ 原文地址:http://blog.chinaunix.net/uid-28977986-id-3 ...
- [016]转--C++拷贝构造函数详解
一. 什么是拷贝构造函数 首先对于普通类型的对象来说,它们之间的复制是很简单的,例如: int a = 100; int b = a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员 ...
- C++拷贝构造函数详解 转
一. 什么是拷贝构造函数 首先对于普通类型的对象来说,它们之间的复制是很简单的,例如: [c-sharp] view plaincopy int a = 100; int b = a; 而类对象与普通 ...
- c++拷贝构造函数(深拷贝,浅拷贝)详解
一.什么是拷贝构造函数 首先对于普通类型的对象来说,它们之间的复制是很简单的,例如: ; int b=a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量. 下面 ...
- C++拷贝构造函数详解
转自:http://blog.csdn.net/lwbeyond/article/details/6202256 对于一个空类,编译器默认生成四个成员函数:默认构造函数.析构函数.拷贝构造函数.赋值函 ...
随机推荐
- 关于ThinkPHP执行长时间任务可能导致PHP使用内存越来越大的问题
ThinkPHP执行长时间任务时,可能导致PHP使用内存越来越大,最后因为内存超出配置限额而程序挂掉. 其实这在很久以前就无意之中发现的一个问题. 3.x之前有这个问题,5.0以后的,应该是已修复了的 ...
- Spark本地运行成功,集群运行空指针异。
一个很久之前写的Spark作业,当时运行在local模式下.最近又开始处理这方面数据了,就打包提交集群,结果频频空指针.最开始以为是程序中有null调用了,经过排除发现是继承App导致集群运行时候无法 ...
- pku1365 Prime Land (数论,合数分解模板)
题意:给你一个个数对a, b 表示ab这样的每个数相乘的一个数n,求n-1的质数因子并且每个指数因子k所对应的次数 h. 先把合数分解模板乖乖放上: ; ans != ; ++i) { ) { num ...
- js中Math之random,round,ceil,floor的用法总结
1.Math.random(); 结果为0-1间的一个随机数(包括0,不包括1) 2.Math.floor(num); 参数num为一个数值,函数结果为num的整数部分(返回小于等于n的最大整数). ...
- k8s监控api调用
k8s监控api调用 curl -s --cacert /etc/kubernetes/ssl/ca.pem -basic -u fengjian:fengjian --insecure -X GET ...
- Java部署环境搭建(Linux)
环境搭建必须jdk.tomcat.mysql(基础) 额外的软件包项目中可能用到 jdk:它包含jre和开发所需完整类库. tomcat:它是一个web容器,项目通常往webapps下扔,便于外界访问 ...
- MP实战系列(六)之代码生成器讲解
MP的代码生成器可谓用"简洁"二字,来形容. 我个人觉得jeesite的代码生成器都不一定比它好用.当然也是由于我个人的习惯. 只需一键执行main方法,就可以生成对应的项目文件, ...
- 乐观锁与悲观锁以及乐观锁的一种实现方式-CAS
首先介绍一些乐观锁与悲观锁: 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁.传统的关系型数据库里边就用到了很 ...
- day85
频率校验 源码分析 声明:基于rest_framework的频率校验 1.首先我们进入到APIView下的dispatch,因为由此方法开始分发的 2.可以看到dispatch方法下有一个initia ...
- Ionic 动态配置url路由的设置
随着Ionic App功能的不断增加,需要路由的url设置就越来越多,不喜欢在config函数中写一堆硬代码,一则不美,二则维护起来也麻烦,能不能把这些数据独立出来呢? 经过查找资料与各种实验,最终找 ...