#C++ Day24 November 6 2025

//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;

}