[Basics of Classes]CPP Learn Data Day 0

原帖地址:https://www.cnblogs.com/Reisentyan/p/19725010

蒸馏舍友学过的西嘎嘎笔记。
他写的笔记真好,好得我看一眼就能记住这是个什么东西,ai加持下。感觉很快就能学完吧(

一、 封装 (Encapsulation)

核心思想: 隐藏内部实现细节,对外提供安全的访问接口和初始化机制。

  • 访问控制:
    • private:用于保护核心数据(如 m_ownerName, m_balance)和内部辅助函数(如 isValidAmount),外部无法直接访问。
    • public:暴露给外部的构造函数和操作接口。
  • 初始化列表: 构造函数使用冒号 : 语法进行成员变量的初始化,支持条件判断(如 initialBalance > 0 ? initialBalance : 0),确保对象创建时的状态合法。
  • 命名规范(参考代码习惯): 类成员函数常使用小驼峰命名(如 isValidAmount),非类函数使用大驼峰。成员变量常加 m_ 前缀以作区分。

参考代码:

//一段关于封装的c++代码
class BankAccount
{
private:
	std::string m_ownerName{ "" };
	double m_balance{ 0 };
	// 类函数用小驼峰,非类函数用大驼峰
	bool isValidAmount(double amount) const {
		return amount > 0;
	}
public:
	//构造函数
	BankAccount(std::string name, double initialBalance)
		: m_ownerName(name), m_balance(initialBalance > 0 ? initialBalance : 0) 
	{};

	~BankAccount(){}

};

二、 组合 (Composition)

核心思想: 将简单的类对象作为成员变量,拼装成复杂的类("Has-a" 关系)。

  • 实现方式:Car 类中,将 Engine 对象和 Wheel 数组作为私有成员变量(m_myEngine, m_myWheels[4])。
  • 任务委托: 复杂对象通过调用内部成员对象的方法来实现功能。例如,Car::drive() 内部调用了 Engine::start()Wheel::roll()

参考代码:

//这是一段关于组合的代码:
class Engine {
public:
	void start()
	{
		std::cout << "源神启动" << '\n';
	}
};

class Wheel {
public:
	void roll()
	{
		std::cout << "转动" << "\n";
	}
};

class Car
{
private:
	Engine m_myEngine;
	Wheel m_myWheels[4];

public:
	void drive() {
		std::cout << "准备出发...\n";
		m_myEngine.start(); // 委托给内部的发动机对象去工作
		for (int i = 0; i < 4; ++i) {
			m_myWheels[i].roll();
		}
		std::cout << "汽车行驶中!\n";
	}
};

三、 多态 (Polymorphism)

核心思想: 通过基类接口调用派生类的具体实现,实现动态绑定。

  • 抽象基类 (Animal):
    • 纯虚函数: 声明后加 = 0(如 virtual void speak() const = 0;),强制要求派生类必须实现该函数。
    • 虚析构函数: 只要类中包含虚函数,析构函数必须声明为 virtual(如 virtual ~Animal() = default;),以防止通过基类指针删除派生类对象时发生内存泄漏。
  • 派生类 (Dog, Cat):
    • 使用 override 关键字修饰重写的虚函数,由编译器检查函数签名是否与基类完全一致,提高代码安全性。
  • 动态绑定与对象切片:
    • 引用传递: 函数参数必须使用引用 &(或指针 *),如 const Animal& animal
    • 避免切片: 如果按值传递,派生类对象会被裁剪为基类对象,丢失原有特性(对象切片)。
    • 虚函数表 (V-Table): 传入引用后,程序在运行时通过查阅对象的虚函数表,动态决定调用哪个具体类(如 Cat)的函数。

参考代码:

//以下为多态相关知识:
class Animal {
public:
	//虚析构函数,只要类里有虚函数,析构函数必须写虚函数
	virtual ~Animal() = default;

	//加上=0,表示是纯虚函数
	virtual void speak()const = 0;
	

};

class Dog :public Animal {
public:
	//override:保证在派生类中声明的重载函数,与基类的虚函数有相同的签名;
	void speak() const override {
		std::cout << "bark" << '\n';
	}
};

class Cat :public Animal {
public:
	void speak() const override {
		std::cout << "mimi" << '\n';
	}
};

//参数中的&是很有必要的,如果没有这个参数,就会发生对象切片的特性
//即,将传入的Animal对象裁剪为基础的Animal对象
void makeThemSpeak(const Animal& animal) {
	animal.speak();
	//这个时候编译器会去查阅传入对象的虚函数表
	//如果它是猫,就会调用猫的函数
	return;
}

四、 const 关键字的用法图鉴

const 用于声明“只读契约”,出现位置不同,约束对象不同:

  1. 放在函数最前面(修饰返回值):
    • 声明返回的数据是只读的,调用者不能修改该返回值。
  2. 放在参数列表中(修饰参数):
    • 例如 void makeThemSpeak(const Animal& animal)
    • 保证在函数内部绝对不会修改传入的参数对象。
  3. 放在成员函数的圆括号后、大括号前(修饰成员函数):
    • 例如 void speak() const override
    • 专属特性: 仅限类的成员函数使用。
    • 底层原理: 成员函数被调用时,编译器会隐式传递一个 this 指针。尾部 const 将这个 this 指针变为只读,保证该函数不会修改类的任何成员变量
    • 独立函数限制: 普通全局函数(如 makeThemSpeak)没有 this 指针,因此不能在尾部加 const,否则编译器会报错。
posted @ 2026-03-16 15:17  粉紫系超人气月兔铃仙  阅读(6)  评论(0)    收藏  举报