目录
- 前言
- 011在栈上进行类的创建与使用
- 012在堆上进行类的创建与使用
- 020类体内声明函数_类体外定义函数
- 030类成员的访问权限的使用
- 040对象大小与成员变量之间的关系
- 050通过nm工具来观察编译后的函数符号名
- 060构造函数与构造函数的重载
- 071构造函数的初始化列表
- 072使用构造函数的初始化列表需要注意事项
- 080初始化const成员变量
- 090简单的析构函数
- 100析构函数执行的时间
- 111数组对象与构造函数调用关系1
- 112数组对象与构造函数调用关系2
- 120封闭类的成员对象的初始化
- 130成员对象消亡的顺序
- 140this指针
- 150static成员静态变量
- 160静态成员函数
- 170const成员变量和成员函数_常成员函数
- 180const对象_常对象
- 190友元函数
- 201简单友元类
- 202复杂友元类
- 210使用不同方法访问类的成员
- 220class与struct的区别
- 231string类简单示例
- 232string类的输入与输出
- 233string类的其他操作1_字符访问与拼接
- 234string类的其他操作2_增删查改
前言
原有的编程环境内容请参考前一篇内容:C++ · 代码笔记1 · 从C到C++
新增工具
在本篇中将会使用到nm工具,在这里需要提一下:
nm是Linux系统中用来列出目标文件中的符号的工具。目标文件是程序编译后的中间文件(gcc编译出来但未链接的“.o”文件),可以是可执行文件、共享库文件或者对象文件。nm工具可以显示这些文件中定义的全局和静态的符号,包括它们的类型和值。
使用nm工具可以帮助开发者了解程序的结构,特别是在调试或者需要了解符号表时。例如,它可以帮助找到未定义的符号,或者检查哪些符号被导出或导入到共享库中。
nm工具的基本用法是:
nm [选项] 文件名
其中,一些常用的选项包括:
-a或--debug-syms:显示所有的符号,不仅仅是全局符号。-g或--extern-only:仅显示外部符号。-u或--undefined-only:仅显示未定义符号。-D或--dynamic:显示动态符号。-C或--demangle:对C++符号进行解码。
nm工具在程序开发、链接和调试过程中非常有用,特别是在处理复杂的链接和库依赖问题时。
011在栈上进行类的创建与使用
相关代码:
#include <iostream>
/**
* @class Student 学生类
*
* @brief 用于表示一个学生,包含学生的ID和姓名。
*
* 学生类提供了基本的成员变量和成员函数,用于存储和输出学生的信息。
*/
class Student
{
public:
// 学生的ID
int ID;
// 学生的姓名
std::string name;
/**
* @fn void say(void)
*
* @brief 输出学生的ID和姓名。
*
* 这个成员函数会将学生的ID和姓名打印到标准输出上。
*/
void say(void)
{
std::cout << "学生的ID为:" << ID << "\n学生的姓名为:" << name << std::endl;
}
};
int main(int argc, char const *argv[])
{
// 创建一个Student对象
Student stu;
// 设置学生的ID
stu.ID = 12345;
// 设置学生的姓名
stu.name = "李华";
// 调用say函数输出学生信息
stu.say();
// 程序正常退出
return 0;
}
运行结果:

012在堆上进行类的创建与使用
相关代码:
#include <iostream>
/**
* @class Student 学生类
*
* @brief 用于表示一个学生,包含学生的ID和姓名。
*
* 学生类提供了基本的成员变量和成员函数,用于存储和输出学生的信息。
*/
class Student
{
public:
// 学生的ID
int ID;
// 学生的姓名
std::string name;
/**
* @fn void say(void)
*
* @brief 输出学生的ID和姓名。
*
* 这个成员函数会将学生的ID和姓名打印到标准输出上。
*/
void say(void)
{
std::cout << "学生的ID为:" << ID << "\n学生的姓名为:" << name << std::endl;
}
};
int main(int argc, char const *argv[])
{
// 动态分配一个Student对象
Student *stu_p = new Student;
// 设置学生的ID
stu_p->ID = 12345;
// 设置学生的姓名
stu_p->name = "李华";
// 调用say函数输出学生信息
stu_p->say();
// 释放动态分配的内存
delete stu_p;
// 程序正常退出
return 0;
}
运行结果:

020类体内声明函数_类体外定义函数
相关代码:
#include <iostream>
class Test
{
public:
// 建议在类体内部对成员函数作声明,而在类体外部进行定义
Test();
~Test();
// 在类体中定义的成员函数会自动成为内联函数,在类体外定义的不会
void say(void)
{
std::cout << "say() 函数" << std::endl;
}
};
Test::Test()
{
std::cout << "构造函数" << std::endl;
}
Test::~Test()
{
std::cout << "析构函数" << std::endl;
}
int main(int argc, char const *argv[])
{
Test t;
t.say();
return 0;
}
运行结果:

030类成员的访问权限的使用
相关代码:
#include <iostream>
/**
* @class Test
* @brief Test类是一个简单的类,用于演示成员变量的设置和获取,以及打印它们的功能。
*
* Test类包含两个私有成员变量:m_number(整数类型)和m_str(字符串类型)。
* 提供了设置这两个成员变量的函数setnumber和setstring,以及获取这两个成员变量的函数getnumber和getstring。
* 此外,还有一个print函数,用于打印成员变量的值。
*/
class Test
{
private:
// 成员变量以m_开头,以m_开头可以看出这是成员变量,与其他函数内部变量区分开来
int m_number; ///< 私有成员变量,用于存储整数数据
std::string m_str; ///< 私有成员变量,用于存储字符串数据
public:
void setnumber(int num); ///< 设置成员变量m_number的值
void setstring(std::string str); ///< 设置成员变量m_str的值
int getnumber(void); ///< 获取成员变量m_number的值
std::string getstring(void); ///< 获取成员变量m_str的值
void print(void); ///< 打印成员变量m_number和m_str的值
};
// 设置成员变量m_number的值
void Test::setnumber(int num)
{
m_number = num;
}
// 设置成员变量m_str的值
void Test::setstring(std::string str)
{
m_str = str;
}
// 获取成员变量m_number的值
int Test::getnumber(void)
{
return m_number;
}
// 获取成员变量m_str的值
std::string Test::getstring(void)
{
return m_str;
}
// 打印成员变量m_number和m_str的值
void Test::print(void)
{
std::cout << "成员变量数据 m_number = " << m_number << ",m_str = " << m_str << std::endl;
}
int main(int argc, char const *argv[])
{
Test t; // 创建Test类的栈上对象
t.setnumber(1); // 设置对象t的成员变量m_number
t.setstring("我是栈上的对象"); // 设置对象t的成员变量m_str
t.print(); // 打印对象t的成员变量值
Test *tp = new Test; // 创建Test类的堆上对象
tp->setnumber(2); // 设置堆上对象tp的成员变量m_number
tp->setstring("我是堆上对象"); // 设置堆上对象tp的成员变量m_str
tp->print(); // 打印堆上对象tp的成员变量值
delete tp; // 释放堆上对象tp,防止内存泄漏
return 0; // 程序正常结束
}
运行结果:

040对象大小与成员变量之间的关系
相关代码:
#include <iostream>
using namespace std;
class Student
{
private:
/* 成员变量按照声明的顺序依次排列,和结构体非常类似,也会有内存对齐的问题。 */
char *m_name; // 私有成员变量,用于存储学生的名字
int m_age; // 私有成员变量,用于存储学生的年龄
float m_score; // 私有成员变量,用于存储学生的成绩
public:
void setname(char *name); // 设置学生名字的函数
void setage(int age); // 设置学生年龄的函数
void setscore(float score); // 设置学生成绩的函数
void show(); // 显示学生信息的函数
};
// 设置学生名字的函数实现
void Student::setname(char *name)
{
m_name = name;
}
// 设置学生年龄的函数实现
void Student::setage(int age)
{
m_age = age;
}
// 设置学生成绩的函数实现
void Student::setscore(float score)
{
m_score = score;
}
// 显示学生信息的函数实现
void Student::show()
{
cout << m_name << "的年龄是" << m_age << ",成绩是" << m_score << endl;
}
int main(int argc, char const *argv[])
{
/**
* 对象的大小只受成员变量的影响,和成员函数没有关系。
*/
// 在栈上创建对象
Student stu;
cout << sizeof(stu) << endl;
// 在堆上创建对象
Student *pstu = new Student();
cout << sizeof(*pstu) << endl;
// 类的大小
cout << sizeof(Student) << endl;
return 0;
}
运行结果:

050通过nm工具来观察编译后的函数符号名
相关代码:
#include <iostream>
using namespace std;
// 相关命令:
// g++ -std=c++11 -c -o main.o -Wall 050通过编译报错观察重命名的函数.cpp
// mn main.o
// 声明一个无参数的display函数
void display();
// 声明一个带有一个整数参数的display函数
void display(int);
// ns命名空间的声明
namespace ns
{
// ns命名空间内的display函数声明
void display();
}
// Demo类的声明
class Demo
{
public:
// Demo类成员函数display的声明
void display();
};
int main()
{
display(); // 调用无参数的display函数
display(1); // 调用带有一个整数参数的display函数
ns::display(); // 调用ns命名空间内的display函数
Demo obj; // 创建Demo类的对象obj
obj.display(); // 调用对象obj的display成员函数
return 0;
}
运行结果:

简单解释:
nm工具的输出列出了目标文件main.o中的符号,以及它们的一些属性。每行的格式通常如下:
地址 符号类型 符号名
其中,地址是符号在文件中的偏移量,符号类型指示了符号的性质,而符号名则是符号的名称。在终端的输出中,符号类型和它们的含义如下:
b .bss:b表示这是一个未初始化的全局或静态变量,存储在.bss段中。t .text:t表示这是一个文本(代码)段。a:表示这是一个归档成员,通常用于表示源文件名。U:表示这是一个未定义的符号,需要在链接时从其他对象文件或库中解析。T:表示这是一个在文本(代码)段中的函数定义。r:表示这是一个只读数据符号。
讲完符号类型,现在看看一些符号的解释:
_GLOBAL__sub_I_main:这是一个由编译器生成的静态构造函数,用于初始化全局或静态对象。_Z41__static_initialization_and_destruction_0ii:这是另一个由编译器生成的函数,用于静态初始化和销毁。_Z7displayi、_Z7displayv、_ZN2ns7displayEv、_ZN4Demo7displayEv:这些是编译后的函数符号名,其中_Z前缀表示这是C++符号,后面的数字或字符表示函数名和参数类型。_ZNSt8ios_base4InitC1Ev、_ZNSt8ios_base4InitD1Ev:这些是std::ios_base::Init类的构造函数和析构函数。_ZStL19piecewise_construct、_ZStL8__ioinit:这些是C++标准库中的符号。__cxa_atexit、__dso_handle、__stack_chk_fail:这些是C++运行时支持函数或符号。main:这是程序的入口点,T表示它是一个定义在文本段中的函数。
未定义的符号(以U标记)将在链接过程中解析,它们通常表示函数或变量引用,其定义在其他对象文件或库中。如果链接器找不到这些符号的定义,链接过程将失败。
上述过程仅进行编译,但未链接,如果编译并链接则会报出以下错误:

060构造函数与构造函数的重载
相关代码:
#include <iostream>
/**
* @class Test
* @brief Test类是一个简单的类,用于演示构造函数和成员函数的使用。
*
* Test类包含两个私有成员变量:一个整数m_number和一个字符串m_str。
* 提供了两个构造函数和一个成员函数print,用于打印成员变量的值。
*/
class Test
{
private:
int m_number; // 私有成员,用于存储整数
std::string m_str; // 私有成员,用于存储字符串
public:
Test(); // 默认构造函数,初始化成员变量
Test(int number, std::string str); // 带参数的构造函数,用于初始化成员变量
void print(void); // 成员函数,用于打印成员变量的值
};
/**
* @brief Test类的默认构造函数
*
* 初始化m_number为0,m_str为一个字符串。
*/
Test::Test()
{
m_number = 0;
m_str = "由无参构造函数初始化此对象,该变量没有任何数据";
}
/**
* @brief Test类的带参数构造函数
*
* @param number 初始化m_number的值
* @param str 初始化m_str的值
*/
Test::Test(int number, std::string str)
{
m_number = number;
m_str = str;
}
/**
* @brief Test类的print成员函数
*
* 打印成员变量m_number和m_str的值。
*/
void Test::print(void)
{
std::cout << "m_number = " << m_number << ",m_str = " << m_str << std::endl;
}
int main(int argc, char const *argv[])
{
Test t1; // 使用默认构造函数创建对象t1
t1.print(); // 打印对象t1的成员变量值
// 在栈上创建对象时,实参位于对象名后面
Test t2(123, "t2"); // 使用带参数构造函数创建对象t2
t2.print(); // 打印对象t2的成员变量值
// 在堆上创建对象时,实参位于类名后面
Test *tp = new Test(321, "tp"); // 使用带参数构造函数在堆上创建对象
tp->print(); // 打印堆上对象tp的成员变量值
delete tp; // 删除堆上创建的对象,释放内存
return 0; // 程序正常退出
}
运行结果:

071构造函数的初始化列表
相关代码:
#include <iostream>
/**
* @class Test
* @brief Test类是一个简单的类,用于演示构造函数和成员函数的使用。
*
* Test类包含两个私有成员变量:一个整数m_number和一个字符串m_str。
* 提供了两个构造函数和一个成员函数print,用于打印成员变量的值。
*/
class Test
{
private:
int m_number; // 私有成员,用于存储整数
std::string m_str; // 私有成员,用于存储字符串
public:
Test(); // 默认构造函数
Test(int number, std::string str); // 带参数的构造函数
void print(void); // 成员函数,用于打印成员变量的值
};
/**
* @brief Test类的默认构造函数
*
* 使用成员初始化列表来初始化m_number为0,m_str为一个特定的字符串。
*/
Test::Test() : m_number(0), m_str("由无参构造函数初始化此对象,该变量没有任何数据")
{
}
/**
* @brief Test类的带参数构造函数
*
* @param number 用于初始化m_number的值
* @param str 用于初始化m_str的值
* 使用成员初始化列表来初始化m_number和m_str。
*/
Test::Test(int number, std::string str) : m_number(number), m_str(str)
{
}
/**
* @brief Test类的print成员函数
*
* 打印成员变量m_number和m_str的值。
*/
void Test::print(void)
{
std::cout << "m_number = " << m_number << ",m_str = " << m_str << std::endl;
}
int main(int argc, char const *argv[])
{
Test t; // 使用默认构造函数创建对象t
t.print(); // 打印对象t的成员变量值
Test *tp = new Test(321, "tp"); // 使用带参数构造函数在堆上创建对象
tp->print(); // 打印堆上对象tp的成员变量值
delete tp; // 删除堆上创建的对象,释放内存
return 0; // 程序正常退出
}
运行结果:

072使用构造函数的初始化列表需要注意事项
相关代码:
#include <iostream>
/* 成员变量的初始化顺序与初始化列表中列出的变量的顺序无关,它只与成员变量在类中声明的顺序有关。 */
class Test
{
private:
int m_number1, m_number2;
public:
// Test类的默认构造函数
Test();
// Test类的单参数构造函数
Test(int number);
// 打印成员变量m_number1和m_number2的值
void print(void);
};
// Test类的默认构造函数实现,初始化m_number2为100,m_number1为m_number2的值
Test::Test() : m_number2(100), m_number1(m_number2) {}
// Test类的单参数构造函数实现,初始化m_number2为传入的参数number,m_number1为m_number2的值
Test::Test(int number) : m_number2(number), m_number1(m_number2)
{
}
// Test类的print函数实现,输出m_number1和m_number2的值
void Test::print(void)
{
std::cout << "m_number1 = " << m_number1 << ",m_number2 = " << m_number2 << std::endl;
}
int main(int argc, char const *argv[])
{
// 创建Test类的对象t,并传入参数100初始化
Test t(100);
// 调用对象t的print函数,输出m_number1和m_number2的值
t.print();
return 0;
}
运行结果:

出现警告及错误数据的简单解释:从上图可以看出,在编译的时候编译器已经警告了构造函数初始化列表中的成员变量初始化顺序与它们在类中声明的顺序不一致,这可能导致未定义行为。在运行的时候果然观察到了初始化的成员变量数据不准确的现象,解决这个问题的方法是按照类中成员变量声明的顺序来初始化它们,以确保在初始化列表中不会引用未初始化的成员。
080初始化const成员变量
相关代码:
#include <iostream>
/* 初始化 const 成员变量的唯一方法就是使用初始化列表 */
class Test
{
private:
// 私有成员变量,用于存储一个常整型数值,只能在构造函数中初始化
const int m_number;
public:
// Test类的构造函数声明,用于初始化成员变量m_number
Test();
// 成员函数print声明,用于输出成员变量m_number的值
void print(void);
};
// Test类的构造函数定义,使用成员初始化列表初始化m_number为1233456
Test::Test() : m_number(1233456)
{
}
// 成员函数print的定义,输出m_number的值
void Test::print(void)
{
std::cout << "m_number = " << m_number << std::endl;
}
// 程序的入口点,主函数main
int main(int argc, char const *argv[])
{
// 创建Test类的对象t
Test t;
// 调用对象t的print成员函数,打印m_number的值
t.print();
// 程序正常退出,返回0
return 0;
}
运行结果:

090简单的析构函数
相关代码:
#include <iostream>
class SimpleClass
{
public:
// 构造函数
SimpleClass()
{
// 分配内存
ptr = new int[10];
std::cout << "构造函数被调用" << std::endl;
}
// 析构函数
~SimpleClass()
{
// 释放内存
delete[] ptr;
std::cout << "析构函数被调用" << std::endl;
}
private:
int *ptr; // 指针成员
};
int main()
{
SimpleClass obj; // 创建对象
return 0;
}
运行结果:

100析构函数执行的时间
相关代码:
#include <iostream>
/**
* @brief 定义一个简单的Demo类
*/
class Demo
{
public:
/**
* @brief 构造函数,接受一个字符串并初始化成员变量
*
* @param s 要初始化的字符串
*/
Demo(std::string s);
/**
* @brief 析构函数,当对象被销毁时调用,输出成员字符串
*/
~Demo();
private:
std::string m_s; ///< 私有成员,存储字符串
};
// 构造函数的实现,使用成员初始化列表初始化m_s
Demo::Demo(std::string s) : m_s(s) {}
// 析构函数的实现,输出成员字符串
Demo::~Demo() { std::cout << m_s << std::endl; }
/**
* @brief 一个简单的函数,创建一个Demo类的局部对象
*/
void func()
{
// 局部对象,生命周期结束时会自动调用析构函数
Demo obj1("1");
}
// 全局对象的声明和定义,程序启动时创建,程序结束时销毁
Demo obj2("2");
int main()
{
// 局部对象,生命周期结束时会自动调用析构函数
Demo obj3("3");
// 使用new关键字动态创建Demo对象,需要手动释放内存
Demo *pobj4 = new Demo("4");
// 调用func函数,创建另一个Demo局部对象
func();
// 输出字符串"main"
std::cout << "main" << std::endl;
// 手动释放new创建的对象内存
delete pobj4;
return 0;
}
/* 输出结果解释:
1、当func函数被调用时,局部对象obj1被创建,并在func函数结束时销毁,所以首先输出"1"。
2、func函数执行完毕后,回到main函数,输出"main"。
3、在main函数的末尾,使用delete关键字销毁了动态创建的对象pobj4,所以接下来输出"4"。
4、然后main函数中的局部对象obj3被销毁,输出"3"。
5、最后,程序结束,全局对象obj2被销毁,输出"2"。 */
运行结果:

111数组对象与构造函数调用关系1
相关代码:
#include <iostream>
using namespace std;
/**
* @brief CSample类,演示了构造函数的重载和对象的创建
*/
class CSample
{
public:
/**
* @brief 无参构造函数
*/
CSample()
{ // 构造函数 1
cout << "构造函数 1 被调用" << endl;
}
/**
* @brief 带一个整型参数的构造函数
* @param n 传递给构造函数的整型参数
*/
CSample(int n)
{ // 构造函数 2
cout << "构造函数 2 被调用" << endl;
}
};
int main()
{
cout << "步骤1" << endl;
CSample array1[2]; // 创建一个包含两个对象的数组,使用无参构造函数
cout << "步骤2" << endl;
CSample array2[2] = {4, 5}; // 创建一个包含两个对象的数组,使用带参构造函数初始化
cout << "步骤3" << endl;
CSample array3[2] = {3}; // 创建一个包含两个对象的数组,第一个对象使用带参构造函数初始化,第二个对象使用无参构造函数初始化
cout << "步骤4" << endl;
CSample *array4 = new CSample[2]; // 在堆上创建一个包含两个对象的数组,使用无参构造函数
delete[] array4; // 释放堆上的数组内存
return 0;
}
运行结果:

112数组对象与构造函数调用关系2
相关代码:
#include <iostream>
/**
* @brief CTest类,演示了构造函数的重载
*/
class CTest
{
public:
/**
* @brief 带一个整型参数的构造函数
* @param n 构造函数的整型参数
*/
CTest(int n)
{
std::cout << "构造函数(1) 被调用,参数:" << n << std::endl;
}
/**
* @brief 带两个整型参数的构造函数
* @param n 第一个整型参数
* @param m 第二个整型参数
*/
CTest(int n, int m)
{
std::cout << "构造函数(2) 被调用,参数:" << n << ", " << m << std::endl;
}
/**
* @brief 无参构造函数
*/
CTest()
{
std::cout << "构造函数(3) 被调用,这是无参构造函数" << std::endl;
}
};
int main()
{
// 三个元素分别用构造函数(1)、(2)、(3) 初始化
CTest array1[3] = {1, CTest(1, 2)};
std::cout << std::endl;
// 三个元素分别用构造函数(2)、(2)、(1)初始化
CTest array2[3] = {CTest(2, 3), CTest(1, 2), 1};
std::cout << std::endl;
// 两个元素指向的对象分别用构造函数(1)、(2)初始化
// pArray[2] 没有初始化,其值是随机的,不知道指向哪里
CTest *pArray[3] = {new CTest(4), new CTest(1, 2)};
std::cout << std::endl;
// 由于指针数组中的元素是动态分配的,需要手动释放内存
for (int i = 0; i < 2; ++i)
{
delete pArray[i];
}
return 0;
}
运行结果:

120封闭类的成员对象的初始化
相关代码:
#include <iostream>
using namespace std;
/**
* 一个类的成员变量如果是另一个类的对象,就称之为“成员对象”。
* 包含成员对象的类叫封闭类(enclosed class)。
*/
/**
* @brief 轮胎类,用于表示轮胎的属性
*/
class Tyre
{
public:
/**
* @brief 构造函数,用于初始化轮胎的半径和宽度
*
* @param radius 轮胎的半径,单位为英寸
* @param width 轮胎的宽度,单位为毫米
*/
Tyre(int radius, int width);
/**
* @brief 显示轮胎的属性
*/
void show() const;
private:
int m_radius; // 半径
int m_width; // 宽度
};
// 构造函数的实现,通过成员初始化列表初始化成员变量
Tyre::Tyre(int radius, int width) : m_radius(radius), m_width(width) {}
// show成员函数的实现,用于输出轮胎的半径和宽度
void Tyre::show() const
{
cout << "轮毂半径:" << this->m_radius << "吋" << endl;
cout << "轮胎宽度:" << this->m_width << "mm" << endl;
}
/**
* @brief 引擎类,用于表示引擎的排量
*/
class Engine
{
public:
/**
* @brief 构造函数,用于初始化引擎的排量
*
* @param displacement 引擎的排量,单位为升
*/
Engine(float displacement = 2.0);
/**
* @brief 显示引擎的排量
*/
void show() const;
private:
float m_displacement; // 排量
};
// 构造函数的实现,通过成员初始化列表初始化成员变量
Engine::Engine(float displacement) : m_displacement(displacement) {}
// show成员函数的实现,用于输出引擎的排量
void Engine::show() const
{
cout << "排量:" << this->m_displacement << "L" << endl;
}
/**
* @brief 汽车类,用于表示汽车的价格、轮胎和引擎属性,在本例中属于封闭类(容器类)
*/
class Car
{
public:
/**
* @brief 构造函数,用于初始化汽车的价格、轮胎和引擎
*
* @param price 汽车的价格,单位为人民币元
* @param radius 轮胎的半径,单位为英寸
* @param width 轮胎的宽度,单位为毫米
*/
Car(int price, int radius, int width);
/**
* @brief 显示汽车的属性,包括价格、轮胎和引擎
*/
void show() const;
private:
int m_price; // 价格
Tyre m_tyre; // 轮胎,成员对象变量
Engine m_engine; // 引擎,成员对象变量
};
// 构造函数的实现,通过成员初始化列表初始化成员变量
Car::Car(int price, int radius, int width) : m_price(price), m_tyre(radius, width) /*指明m_tyre对象的初始化方式*/ {};
// show成员函数的实现,用于输出汽车的各属性
void Car::show() const
{
cout << "价格:" << this->m_price << "¥" << endl;
this->m_tyre.show();
this->m_engine.show();
}
/**
* @brief 主函数,程序入口
*
* @return int 返回状态码,0表示正常退出
*/
int main()
{
Car car(200000, 19, 245); // 创建汽车对象,价格200000元,轮胎半径19英寸,宽度245毫米
car.show(); // 显示汽车属性
return 0;
}
运行结果:

130成员对象消亡的顺序
相关代码:
#include <iostream>
using namespace std;
/**
* 成员对象析构函数的执行次序和构造函数的执行次序相反,
* 即先调用构造函数的对象,后执行析构函数
*/
// 轮胎类
class Tyre
{
public:
// 轮胎类的构造函数
Tyre() { cout << "轮胎构造函数" << endl; }
// 轮胎类的析构函数
~Tyre() { cout << "轮胎析构函数" << endl; }
};
// 引擎类
class Engine
{
public:
// 引擎类的构造函数
Engine() { cout << "引擎构造函数" << endl; }
// 引擎类的析构函数
~Engine() { cout << "引擎析构函数" << endl; }
};
// 汽车类
class Car
{
private:
Engine engine; // 引擎成员变量
Tyre tyre; // 轮胎成员变量
public:
// 汽车类的构造函数
Car() { cout << "汽车构造函数" << endl; }
// 汽车类的析构函数
~Car() { cout << "汽车析构函数" << endl; }
};
// 程序的主函数
int main()
{
Car car; // 创建汽车对象
return 0; // 程序正常结束
}
运行结果:

140this指针
相关代码:
#include <iostream>
/* this指针是一个 const 指针,它指向当前对象,通过它可以访问当前对象的所有成员。 */
class Test
{
private:
// 定义私有成员变量number
int number;
public:
// 设置number成员变量的值
void setnumber(int numnber);
// 获取number成员变量的值
int getnumber(void);
// 打印当前对象的地址
void print_function_address(void);
};
// Test类的成员函数定义,用于设置number成员变量的值
void Test::setnumber(int number)
{
// 使用this指针将传入的参数值赋给成员变量number
this->number = number;
}
// Test类的成员函数定义,用于获取number成员变量的值
int Test::getnumber(void)
{
// 返回成员变量number的值
return this->number;
}
// Test类的成员函数定义,用于打印当前对象的地址
void Test::print_function_address(void)
{
// 使用this指针获取当前对象的地址,并输出
std::cout << "使用this指针查看当前的地址为:" << this << std::endl;
}
// 程序的入口函数
int main(int argc, char const *argv[])
{
// 创建Test类的指针,并动态分配内存
Test *tp = new Test;
// 通过指针调用setnumber函数,设置number值为1
tp->setnumber(1);
// 输出number的值
std::cout << "tp的成员变量的number值为:" << tp->getnumber() << std::endl;
// 调用print_function_address函数打印对象地址
tp->print_function_address();
// 输出指针本身的地址
std::cout << "在main函数中查看当前的地址为:" << tp << std::endl;
// 释放动态分配的内存
delete tp;
// 程序正常退出
return 0;
}
运行结果:

150static成员静态变量
相关代码:
#include <iostream>
class MyClass
{
public:
// 静态成员变量声明
static int staticVar;
// 每创建一个对象,成员静态变量就递增 1
MyClass()
{
++staticVar;
}
// 静态成员函数声明
static void staticFunc()
{
std::cout << "静态成员函数访问静态变量: " << staticVar << std::endl;
}
};
// 1、静态成员变量的定义和初始化,必须在类外进行
// 2、此类所有的对象共享此成员静态变量
// 3、成员变量不随对象的创建而分配内存,也不随对象的销毁而释放内存。
int MyClass::staticVar = 0;
int main()
{
// 访问静态成员变量,不需要创建对象实例
std::cout << "直接访问静态成员变量: " << MyClass::staticVar << std::endl;
// 通过对象实例访问静态成员变量
MyClass obj;
std::cout << "通过对象实例访问静态成员变量: " << obj.staticVar << std::endl;
// 调用静态成员函数
MyClass::staticFunc();
return 0;
}
运行结果:

160静态成员函数
相关代码:
#include <iostream>
#include <iomanip> // 包含此头文件以使用 std::setprecision
/* 静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。 */
/**
* @class Test
* @brief Test 类用于演示静态成员变量和静态成员函数的使用。
*
* 该类包含两个静态成员变量 int_number 和 double_number,
* 以及两个静态成员函数 print_static_number_val 和 print_test。
*/
class Test
{
private:
static int int_number; // 静态整型成员变量,用于存储整数值
static double double_number; // 静态双精度浮点型成员变量,用于存储浮点数值
public:
/**
* @fn static void print_static_number_val(void)
* @brief 打印静态成员变量 int_number 和 double_number 的值。
*
* 该函数使用 std::cout 输出静态成员变量 int_number 和 double_number 的值,
* 并设置浮点数的输出精度为两位小数。
*/
static void print_static_number_val(void);
/**
* @fn static void print_test(void)
* @brief 打印一条简单的信息,用于演示静态成员函数的调用。
*
* 该函数使用 std::cout 输出一条简单的信息,表明这是一个静态成员函数。
*/
static void print_test(void);
};
// 静态整型成员变量的定义和初始化
int Test::int_number = 1;
// 静态双精度浮点型成员变量的定义和初始化
double Test::double_number = 2.0;
// 静态成员函数 print_static_number_val 的定义
void Test::print_static_number_val()
{
// 使用 std::cout 输出静态成员变量 int_number 和 double_number 的值,
// 并设置浮点数的输出精度为两位小数
std::cout << "int_number = " << int_number
<< ",double_number = "
<< std::fixed // 设置浮点数输出为固定点表示法
<< std::setprecision(2) // 设置浮点数输出精度为两位小数
<< double_number // 输出静态成员变量 double_number 的值
<< std::endl; // 输出换行符
}
// 静态成员函数 print_test 的定义
void Test::print_test(void)
{
// 使用 std::cout 输出一条简单的信息
std::cout << "这是简单的静态成员函数。" << std::endl;
}
// 程序的入口点
int main(int argc, char const *argv[])
{
// 调用 Test 类的静态成员函数 print_test
Test::print_test();
// 调用 Test 类的静态成员函数 print_static_number_val
Test::print_static_number_val();
// 程序正常退出
return 0;
}
运行结果:

170const成员变量和成员函数_常成员函数
相关代码:
#include <iostream>
/* 函数头部的结尾加上 const 表示常成员函数.
* 这种函数只能读取成员变量的值,而不能修改成员变量的值。
*/
/**
* @class Test
* @brief Test类用于演示基本的类成员操作。
*
* Test类包含一个私有整型成员变量m_number,
* 以及两个公有成员函数setvalue和ret_val,
* 分别用于设置和获取成员变量m_number的值。
*/
class Test
{
private:
int m_number; // 私有整型成员变量,存储对象的数值
public:
/**
* @fn void setvalue(int number)
* @brief 设置私有成员变量m_number的值。
*
* @param number 要设置的数值。
*/
void setvalue(int number);
/**
* @fn int ret_val(void) const
* @brief 获取私有成员变量m_number的值。
*
* @return 返回私有成员变量m_number的当前值。
*/
int ret_val(void) const;
};
// Test类成员函数setvalue的实现,用于设置私有成员变量m_number的值
void Test::setvalue(int number)
{
m_number = number; // 将传入的数值赋值给私有成员变量m_number
}
// Test类成员函数ret_val的实现,用于获取私有成员变量m_number的值
int Test::ret_val(void) const
{
return m_number; // 返回私有成员变量m_number的值
}
// 程序的入口点
int main(int argc, char const *argv[])
{
Test t; // 创建Test类的对象t
t.setvalue(1); // 调用对象t的setvalue函数,设置m_number为1
std::cout << "对象的number值为:" << t.ret_val() << std::endl; // 输出对象t的m_number值
return 0; // 程序正常退出
}
运行结果:

180const对象_常对象
相关代码:
#include <iostream>
using namespace std;
/**
* @class Student 学生类
*
* @brief 用于表示一个学生,包含学生的姓名、年龄和成绩
*
* 该类提供了获取学生信息的接口,并且可以显示学生的信息。
*/
class Student
{
public:
/**
* @brief 构造函数
*
* @param name 学生姓名
* @param age 学生年龄
* @param score 学生成绩
*/
Student(std::string name, int age, float score) : m_name(name), m_age(age), m_score(score) {}
public:
/**
* @brief 显示学生信息
*
* 输出学生的姓名、年龄和成绩。
*/
void show();
/**
* @brief 获取学生姓名
*
* @return const std::string 学生姓名
*/
std::string getname() const;
/**
* @brief 获取学生年龄
*
* @return int 学生年龄
*/
int getage() const;
/**
* @brief 获取学生成绩
*
* @return float 学生成绩
*/
float getscore() const;
private:
std::string m_name; // 学生姓名
int m_age; // 学生年龄
float m_score; // 学生成绩
};
// 显示学生信息
void Student::show()
{
cout << m_name << "的年龄是" << m_age << ",成绩是" << m_score << endl;
}
// 获取学生姓名
std::string Student::getname() const
{
return m_name;
}
// 获取学生年龄
int Student::getage() const
{
return m_age;
}
// 获取学生成绩
float Student::getscore() const
{
return m_score;
}
/**
* @brief 主函数
*
* @return int 运行结果
*/
int main()
{
/** 一旦将对象定义为常对象之后,就只能调用类的 const 成员了
* 包括 const 成员变量和 const 成员函数。
**/
// 创建一个const类型的Student对象,姓名为"小明",年龄为15,成绩为90.6
const Student stu(std::string("小明"), 15, 90.6);
// stu.show(); // 错误,const对象不能调用非const成员函数
// 输出const对象的信息
std::cout << stu.getname() << "的年龄是" << stu.getage() << ",成绩是" << stu.getscore() << endl;
// 创建一个const指针指向Student对象,姓名为"李磊",年龄为16,成绩为80.5
const Student *pstu = new Student("李磊", 16, 80.5);
// pstu -> show(); // 错误,const指针不能调用非const成员函数
// 输出const指针指向对象的信息
cout << pstu->getname() << "的年龄是" << pstu->getage() << ",成绩是" << pstu->getscore() << endl;
// 清理内存并退出程序
delete pstu;
return 0;
}
运行结果:

190友元函数
相关代码:
#include <iostream>
using namespace std;
/**
* @class Student 学生类
*
* @brief 用于表示一个学生,包含学生的姓名、年龄和成绩
*
* 该类提供了通过友元函数显示学生信息的接口。
*/
class Student
{
public:
/**
* @brief 构造函数
*
* @param name 学生姓名
* @param age 学生年龄
* @param score 学生成绩
*/
Student(std::string name, int age, float score) : m_name(name), m_age(age), m_score(score) {}
public:
/**
* @brief 显示学生信息的友元函数
*
* @param pstu 指向Student对象的指针
*/
// 友元函数可以访问当前类中的所有成员,包括 public、protected、private 属性的。
friend void show(Student *pstu);
private:
std::string m_name; // 学生姓名
int m_age; // 学生年龄
float m_score; // 学生成绩
};
/**
* @brief 显示学生信息的友元函数
*
* @param pstu 指向Student对象的指针
*/
void show(Student *pstu)
{
cout << pstu->m_name << "的年龄是 " << pstu->m_age << ",成绩是 " << pstu->m_score << endl;
}
/**
* @brief 主函数
*
* @return int 运行结果
*/
int main()
{
// 创建一个Student对象,姓名为"小明",年龄为15,成绩为90.6
Student stu(std::string("小明"), 15, 90.6);
// 调用友元函数显示学生信息
show(&stu);
// 创建一个指向Student对象的指针,姓名为"李磊",年龄为16,成绩为80.5
Student *pstu = new Student(std::string("李磊"), 16, 80.5);
// 调用友元函数显示学生信息
show(pstu);
// 清理内存
delete pstu;
return 0;
}
运行结果:

201简单友元类
相关代码:
#include <iostream>
using namespace std;
/** 友元类中的所有成员函数都是另外一个类的友元函数。
* 在本例中,Friend 类是 Box 类的友元类,因为Friend 类可以访问 Box 类的所有成员
* 注意!开发中尽量不要使用友元类,因为这会破坏封装。
*/
// 前向声明,因为在 Box 类中会用到 Friend 类
class Friend;
// Box 类
class Box
{
// Friend 类是 Box 的友元类
friend class Friend;
public:
Box(double l) : length(l) {}
private:
double length; // Box 的私有成员
};
// Friend 类
class Friend
{
public:
void displayLength(Box &b)
{
// 由于 Friend 是 Box 的友元类,它可以访问 Box 的私有成员
cout << "长度为: " << b.length << endl;
}
};
int main()
{
Box box(10.0); // 创建 Box 对象
Friend friendObj; // 创建 Friend 对象
// 使用 Friend 类的成员函数来访问 Box 类的私有成员
friendObj.displayLength(box);
return 0;
}
运行结果:

202复杂友元类
相关代码:
#include <iostream>
using namespace std;
// 提前声明Address类,以便在Student类中使用
class Address;
/**
* @class Student 学生类
*
* @brief 用于表示一个学生,包含学生的姓名、年龄和成绩
*/
class Student
{
public:
// 构造函数
Student(std::string name, int age, float score);
public:
// 显示学生信息及其家庭住址
void show(Address *addr);
private:
std::string m_name; // 学生姓名
int m_age; // 学生年龄
float m_score; // 学生成绩
};
// Address类的提前声明
class Address;
/**
* @class Address 地址类
*
* @brief 用于表示一个地址,包含省份、城市和区域
*/
class Address
{
public:
// 构造函数
Address(std::string province, std::string city, std::string district);
public:
// 将Student类声明为Address类的友元类
friend class Student;
private:
std::string m_province; // 省份
std::string m_city; // 城市
std::string m_district; // 区域
};
// Student类的构造函数实现
Student::Student(std::string name, int age, float score) : m_name(name), m_age(age), m_score(score) {}
// Student类的show成员函数实现,显示学生信息及其家庭住址
void Student::show(Address *addr)
{
cout << m_name << "的年龄是 " << m_age << ",成绩是 " << m_score << endl;
cout << "家庭住址:" << addr->m_province << "省" << addr->m_city << "市" << addr->m_district << "区" << endl;
}
// Address类的构造函数实现
Address::Address(std::string province, std::string city, std::string district)
{
m_province = province;
m_city = city;
m_district = district;
}
// 主函数
int main()
{
// 创建Student对象和Address对象
Student stu("小明", 16, 95.5f);
Address addr("陕西", "西安", "雁塔");
// 显示学生信息及其家庭住址
stu.show(&addr);
std::cout << std::endl;
// 创建Student指针和Address指针,指向动态分配的对象
Student *pstu = new Student("李磊", 16, 80.5);
Address *paddr = new Address("河北", "衡水", "桃城");
// 显示学生信息及其家庭住址
pstu->show(paddr);
// 释放动态分配的内存
delete pstu;
delete paddr;
return 0;
}
运行结果:

210使用不同方法访问类的成员
相关代码:
#include <iostream>
using namespace std;
/**
* 在类的作用域之外,普通的成员只能通过对象(可以是对象本身,也可以是对象指针或对象引用)来访问
* 静态成员既可以通过对象访问,又可以通过类访问
* typedef 定义的类型只能通过类来访问。
*/
class A
{
public:
// 类型定义,将int类型定义别名为INT
// typedef 定义的类型只能通过类来访问。
typedef int INT;
// 声明一个静态成员函数show
static void show();
// 声明一个成员函数work
void work();
};
// 类A的静态成员函数show的定义,输出字符串"show()"并换行
void A::show() { cout << "show()" << endl; }
// 类A的成员函数work的定义,输出字符串"work()"并换行
void A::work() { cout << "work()" << endl; }
int main()
{
A a;
a.work(); // 通过对象访问普通成员
a.show(); // 通过对象访问静态成员
A::show(); // 通过类访问静态成员
A::INT n = 10; // 通过类访问 typedef 定义的类型
std::cout << "n = " << n << std::endl;
return 0;
}
运行结果:

220class与struct的区别
相关代码:
#include <iostream>
using namespace std;
// 如果下面写成“ class Student ”,那么将不会通过编译
// 因为class默认所有成员都是私有的,仅能被本类访问,哪怕是实例化对象也不允许访问
struct Student
{
// 构造函数
Student(std::string name, int age, float score);
// 成员函数,用于打印学生信息
void show(void);
// 学生姓名
std::string m_name;
// 学生年龄
int m_age;
// 学生成绩
float m_score;
};
/**
* @brief Student类的构造函数
* @param name 学生姓名
* @param age 学生年龄
* @param score 学生成绩
*/
Student::Student(std::string name, int age, float score) : m_name(name), m_age(age), m_score(score) {}
/**
* @brief 显示学生信息
*/
void Student::show(void)
{
cout << m_name << "的年龄是" << m_age << ",成绩是" << m_score << endl;
}
int main(int argc, char const *argv[])
{
// 创建Student对象
Student stu("小明", 15, 92.5f);
// 调用show函数打印学生信息
stu.show();
// 动态分配内存创建Student对象
Student *pstu = new Student("李华", 16, 96);
// 通过指针调用show函数打印学生信息
pstu->show();
// 释放动态分配的内存
delete pstu;
// 程序正常退出
return 0;
}
运行结果:

231string类简单示例
相关代码:
#include <stdio.h>
#include <iostream>
#include <string>
int main(int argc, char const *argv[])
{
std::string s1; // 创建一个空的字符串s1
std::string s2 = "test text"; // 创建一个字符串s2,并初始化为"test text"
std::string s3 = s2; // 创建一个字符串s3,并初始化为s2的副本
std::string s4(10, 'x'); // 创建一个字符串s4,包含5个字符'o'
std::cout << "s2:" << s2 << "\ns3:" << s3 << "\ns4:" << s4 << std::endl;
std::cout << "s4的长度为:" << s4.length() << std::endl;
const char *c_string = s4.c_str();
printf("将s4转换成c语言风格的字符串为:%s\n", c_string);
return 0;
}
运行结果:

232string类的输入与输出
相关代码:
#include <iostream>
#include <string>
int main(int argc, char const *argv[])
{
std::string user_string; // 声明一个字符串变量,用于存储用户输入的内容
// 提示用户输入内容
std::cout << "请输入内容:";
// 从标准输入读取字符串,直到遇到空白字符(空格、制表符、换行符)
std::cin >> user_string;
// 输出用户输入的内容,并在末尾添加换行符
std::cout << "用户输入的内容为:" << user_string << std::endl;
return 0;
}
运行结果:

233string类的其他操作1_字符访问与拼接
相关代码:
#include <iostream>
#include <string>
int main(int argc, char const *argv[])
{
std::string ss = "1234567890"; // 初始化一个字符串ss,内容为"1234567890"
std::cout << "s1的字符为:"; // 输出提示信息
for (int i = 0, len = ss.length(); i < len; i++) // 使用for循环遍历字符串ss的每个字符
{
std::cout << ss[i] << " "; // 输出当前字符,后面跟一个空格
}
std::cout << std::endl; // 输出换行符
ss[0] = '0'; // 将字符串ss的第一个字符修改为'0'
std::cout << "更改后的s1为:" << ss << std::endl; // 输出修改后的字符串ss
std::string s1 = "first "; // 初始化字符串s1,内容为"first "
std::string s2 = "second "; // 初始化字符串s2,内容为"second "
const char *s3 = "third "; // 初始化一个常量字符指针s3,指向字符串"third "
char s4[] = "fourth "; // 初始化一个字符数组s4,内容为"fourth "
char ch = '@'; // 初始化一个字符ch,值为'@'
std::string s5 = s1 + s2; // 使用+运算符连接字符串s1和s2,结果存储在s5中
std::string s6 = s1 + s3; // 使用+运算符连接字符串s1和s3(指向的字符串),结果存储在s6中
std::string s7 = s1 + s4; // 使用+运算符连接字符串s1和s4(字符数组),结果存储在s7中
std::string s8 = s1 + ch; // 使用+运算符连接字符串s1和字符ch,结果存储在s8中
std::cout << "s5 = " << s5 << std::endl
<< "s6 = " << s6 << std::endl
<< "s7 = " << s7 << std::endl
<< "s8 = " << s8 << std::endl;
return 0;
}
运行结果:

234string类的其他操作2_增删查改
相关代码:
#include <iostream>
#include <string>
int main(int argc, char const *argv[])
{
// ============ 插入 ============
std::string s1, s2, s3; // 定义三个字符串变量
s1 = s2 = "1234567890"; // 初始化s1和s2为"1234567890"
s3 = "yyy"; // 初始化s3为"yyy"
s1.insert(0, s3); // 在s1的开始位置插入字符串s3
s2.insert(3, s3); // 在s2的索引3的位置插入字符串s3
std::cout << "s1 = " << s1 << std::endl // 打印s1的值
<< "s2 = " << s2 << std::endl; // 打印s2的值
std::cout << std::endl;
// ============ 删除 ============
s1 = s2 = s3 = "1234567890"; // 重新初始化s1, s2, s3为"1234567890"
s2.erase(5); // 删除s2中从索引5开始直到字符串末尾的所有字符
s3.erase(5, 3); // 删除s3中从索引5开始的3个字符
std::cout << "s1 = " << s1 << std::endl // 打印s1的值
<< "s2 = " << s2 << std::endl // 打印s2的值
<< "s3 = " << s3 << std::endl; // 打印s3的值
std::cout << std::endl;
// ============ 查找 ============
s1 = "first second third"; // 初始化s1为"first second third"
s2 = "second"; // 初始化s2为"second"
unsigned long index = s1.find(s2, 5); // 从s1的索引5开始查找s2,返回s2在s1中首次出现的位置
if (index < s1.length()) // 如果找到了且索引在s1的有效范围内
std::cout << "找到s2出现在s1的位置:" << index << std::endl; // 打印找到的位置
else
std::cout << "没找到字符串" << std::endl; // 如果没找到,打印提示信息
index = s1.rfind(s2, 6); // 从s1的索引6开始向后查找s2,返回s2在s1中最后一次出现的位置
if (index < s1.length()) // 如果找到了且索引在s1的有效范围内
std::cout << "找到s2出现在s1的位置:" << index << std::endl; // 打印找到的位置
else
std::cout << "没找到字符串" << std::endl; // 如果没找到,打印提示信息
s1 = "first second second third"; // 重新初始化s1为"first second second third"
s2 = "asecond"; // 初始化s2为"asecond"
index = s1.find_first_of(s2); // 查找s2中任意字符在s1中首次出现的位置
if (index < s1.length()) // 如果找到了且索引在s1的有效范围内
std::cout << "s2与s1共同出现的字符在位置:" << index << std::endl; // 打印找到的位置
else
std::cout << "没找到字符串" << std::endl; // 如果没找到,打印提示信息
return 0;
}
运行结果:
