//Objetct – Oriented 5 – 1 construct a function
//构造函数
#include <iostream>
#include <string>
using namespace std;
/*
构造函数需要注意的点
1.函数名称和类名保持一致
2.返回值类型 不需要写
3.构造函数可以有参数
*/
class Hero {
public:
//默认构造函数
Hero() {
m_Name = “”;
m_SkillCount=4;
m_Speed = 100;
cout << “默认构造函数:Hero 构造完毕!” << endl;
}
//有参构造函数1
//想要把它的名字构造进来
Hero(string name) {
m_Name = name;
m_SkillCount = 4;
m_Speed = 100;
cout << “有参构造函数1:Hero 构造完毕!” << endl;
}
//有参构造函数2
Hero(string name,int skillcount) {
m_Name = name;
m_SkillCount = skillcount;
m_Speed = 100;
cout << “有参构造函数2:Hero 构造完毕!” << endl;
}
private:
string m_Name;
int m_SkillCount;
int m_Speed;
};
int main() {
Hero h1;//一个类在实例化对象的时候 需要对对象进行初始化 这些构造函数需要在初始化里面进行
Hero h2(“Vito”);//它能够识别括号里面每个参数的类型是否和上面的匹配
Hero h3(); //这不是一个函数构造过程 这其实是一个函数声明 类似于int main(); int work();
Hero h4{};//无参可以这样构造
Hero h5 = Hero(“Vito”);//把string传进来 实际上这里生成了一个匿名对象 生成匿名对象后它赋值给h5
Hero h6{ “Elon”,4 }; //这个也会调用有参构造函数
return 0;
}
//Objetct – Oriented 5 – 2 destructor
//析构函数
#include <iostream>
using namespace std;
//析构函数是和构造函数相对的
//构造函数是对对象进行初始化 析构函数就是对对象进行反初始化 就是做数据清理
//析构函数是在对象被清理前由系统进行自动调用
/*
析构函数注意点
1.函数名称和类名一致,并且在最前面加上一个~波浪号
2.函数返回值不需要写
3.不能有参数
析构函数难点在面向对象的继承里面
组合起来 封装 继承 多态 就会有非常多种情况
*/
class Hero {
public:
//构造函数
Hero() {//不做赋值
cout << “Hero的默认构造函数调用完毕!” << endl;
}
//对象在创建时可能需要赋一些值 但是我在销毁的时候 其实不需要赋任何参数
//析构函数
~Hero() {//析构函数就是在构造函数前加上一个波浪号 析构函数的特点就是里面没有参数
cout << “Hero的析构函数调用完毕!” << endl;
}
};
void test() {
Hero h;
}
int main() {
test();//这个对象的作用域结束时 系统会自动调用析构函数
Hero h;
int a;
cin >> a; //程序没有结束的话不 return 0 就不会调用析构函数
return 0;
}
这段代码展示了 C++ 中函数返回自定义类型(Hero类)对象的用法,核心涉及对象返回机制和编译器优化(RVO/NRVO) 的概念,我们可以从以下几个角度理解:
1. 代码逻辑解析
- test3()函数:返回类型为Hero,函数内部创建一个Hero对象h(构造参数为 40),然后将该对象作为返回值返回。
- func3()函数:调用test3(),并将返回的Hero对象赋值给新的Hero对象h。
2. 关键概念:对象返回与复制构造
正常情况下(不考虑编译器优化),当test3()返回局部对象h时,会发生:
- test3()中创建h(调用Hero的构造函数,参数 40)。
- 返回时,会生成一个临时对象(调用复制构造函数,用h复制初始化临时对象)。
- func3()中,用这个临时对象初始化h(再次调用复制构造函数)。
- 临时对象被销毁(调用析构函数)。
但实际中,你可能观察不到这么多复制操作,这涉及编译器的优化。
3. 编译器优化:RVO/NRVO
- RVO(Return Value Optimization,返回值优化):编译器会直接在目标对象(func3()中的h)的内存位置构造test3()中的对象,避免临时对象和复制操作。
- NRVO(Named Return Value Optimization,具名返回值优化):针对函数中有名的局部对象(如test3()中的h)的优化,效果和 RVO 类似。
这就是代码注释中提到 “需要关掉 RVO 优化(/Zc:nrvo-)” 的原因 —— 如果不关闭优化,编译器会省略复制构造函数的调用(直接构造最终对象),导致无法观察到完整的对象复制过程(仅用于教学演示)。
4. 总结
这段代码的核心目的是演示 “函数返回自定义类型对象” 的机制,而关闭编译器优化是为了更清晰地观察对象的创建、复制和销毁过程。在实际开发中,通常不需要关闭这种优化,因为它能提升程序性能(减少不必要的对象复制)。
//Objetct – Oriented 5 – 3 copy constructor
//拷贝构造函数
#include <iostream>
using namespace std;
//拷贝构造函数也是一种构造函数 它比较特殊
//用一个对象构造出另一个对象
/*
拷贝构造函数的定义
类名(const 类型& 变量名){}
引用的目的是避免在调用过程中 传参传进来的过程中多一次拷贝 所以传个别名进来 而const是为了传进来的参数不会被改变
为了通过a对象来创建b对象 结果我把a对象改掉就不合理了 加const就变成一个常量就不能在内部改了
*/
class Hero {
public:
//默认构造函数
Hero() {
m_Hp=100;//变量初始化
cout << “Hero 默认构造函数调用完毕!” << endl;
}
//有参构造函数 为了给成员变量赋值
Hero(int hp) {
m_Hp = hp;
cout << “Hero 有参构造函数调用完毕!” << endl;
}
//拷贝构造函数要用一个函数来构造另一个函数 它一定是一个有参构造函数 要传另外一个对象进来
Hero(const Hero& h) {//拷贝构造函数 需要const Hero& h 这种写法
//引用的目的是避免在调用过程中 传参传进来的过程中多一次拷贝 所以传个别名进来 而const是为了传进来的参数不会被改变
//为了通过a对象来创建b对象 结果我把a对象改掉就不合理了
//h.m_Hp = 4;//报错
m_Hp = h.m_Hp;//不能改不代表不能拿 可以拿出来赋值给当前英雄的血量
//把所有成员变量从const Hero& h对象上拷贝过来 就完成一次拷贝了
cout << “Hero 拷贝构造函数调用完毕!” << endl;
}
//析构函数
~Hero() {
cout << “Hero 析构函数调用完毕!” << endl;
}
private:
int m_Hp;
};
/*
拷贝构造函数调用时机
1.用已经创建的对象来初始化对象
2.函数的传参
3.函数的返回值
*/
//1.用已经创建的对象来初始化对象
void func1() {
cout << “————-func1————-” << endl;
Hero h1(20);//实例化一个对象 h1
Hero h2(h1);//用拷贝构造函数实例化一个对象 通过一个h1对象来生成h2对象
//等这个函数执行完后 h1和h2都析构掉了
}
void test1(Hero h) {//h是形式参数 在函数定义时用来接收传入值的占位符
}
void test2(Hero* h) {//h是形式参数 在函数定义时用来接收传入值的占位符
}
//2.函数的传参
void func2() {
cout << “————-func2————-” << endl;
Hero h1;
//test1(h1);//h1作为是一个实参
//当函数传参有这个对象类型的时候 进去就会调用一次拷贝构造函数
test2(&h1);//这样不会调用拷贝构造函数 只有h1被析构掉
//因为指针代表了传进来对象的地址 并没有生成一个新的对象 所以并没有调用拷贝构造函数
//拷贝构造函数的前提是 我要生成一个新的对象
}
//3.函数的返回值
//再来实现一个接口
Hero test3() {//返回值是一个Hero类型
Hero h(40);
return h;//把这个对象作为一个返回值返回出去了
}
void func3() {
cout << “————-func3————-” << endl;
Hero h=test3();//可以生成同一个返回值类型的对象
//需要关掉 rvo优化 /Zc:nrvo-
//不然编译器认为不调更加高效 逻辑也是对的
}
int main() {
func1();
func2();
func3();
return 0;
}
//Objetct – Oriented 5 – 4 initialization list
//初始化列表
//主要给构造函数进行初始化用
/*
初始话列表的语法
构造函数(传参1,传参2): 成员变量1(传参1),成员变量2(传参2){}
*/
#include <iostream>
#include <string>
using namespace std;
class Hero {
public:
/*Hero(string name, int hp) {
m_Name = name;
m_Hp = hp;
}*/
//Hero(string name, int hp):m_Name(“猴子”),m_Hp(50) {//这里走初始化逻辑
//
//}
Hero(string name, int hp,int speed) :m_Name(name), m_Hp(hp),m_Speed(speed) {
}
void Print() {
cout << “英雄:” << m_Name << “的血量是” << m_Hp << “,速度是” <<m_Speed<< endl;
}
private:
string m_Name;
int m_Hp;
int m_Speed;
};
int main() {
Hero h(“Vito”, 100,10);
h.Print();
return 0;
}