Ch 19: 类基础
- 深入理解 Python 类和 C++ 类的本质区别
- 掌握类的基本结构、访问控制和成员函数
- 理解 this 指针的用法和链式调用
- 学会设计简洁清晰的类接口
- 理解 const 成员函数的重要性
19.1 Python 类回顾
Section titled “19.1 Python 类回顾”Python 类的基本结构
Section titled “Python 类的基本结构”# Python - 简单直接的面向对象class Person: def __init__(self, name: str, age: int): self.name = name # 公开属性 self._age = age # 受保护(约定) self.__id = 0 # 私有(名称改写)
def greet(self) -> str: return f"Hello, I'm {self.name}"
@property def age(self) -> int: return self._age
p = Person("Alice", 30)print(p.greet())Python 类的特点
Section titled “Python 类的特点”# Python 的类非常灵活class DynamicClass: pass
# 可以在运行时添加属性obj = DynamicClass()obj.new_attr = "value"
# 多继承class A: passclass B: passclass C(A, B): pass
# 方法解析顺序(MRO)class Base: def method(self): return "Base"
class Derived(Base): def method(self): return "Derived"
d = Derived()print(d.method()) # DerivedPython 的封装(约定 vs 强制)
Section titled “Python 的封装(约定 vs 强制)”# Python - 依赖约定class Account: def __init__(self, balance): self.balance = balance # 公开,谁都能改 self._transactions = [] # 约定受保护 self.__pin = 1234 # 名称改写,但仍可访问(_Account__pin)
# 但Python没有真正的私有 print(account._Account__pin) # 可以访问19.2 C++ 类基础
Section titled “19.2 C++ 类基础”#include <iostream>#include <string>
class Person {public: // 构造函数 Person(const std::string& name, int age) : name_(name), age_(age) {}
// 成员函数 std::string greet() const { return "Hello, I'm " + name_; }
// Getter(类似 @property) std::string name() const { return name_; } int age() const { return age_; }
private: // 私有成员(无法从类外访问) std::string name_; int age_;};
int main() { Person p("Alice", 30); std::cout << p.greet() << std::endl; std::cout << p.name() << " is " << p.age() << " years old\n";}struct vs class
Section titled “struct vs class”// struct - 默认 public 成员struct PointA { int x; // 公开 int y;};
PointA p1;p1.x = 10; // OK
// class - 默认 private 成员class PointB { int x; // 私有! int y;};
PointB p2;p2.x = 10; // ❌ 编译错误!
// 约定:struct 用于纯数据结构,class 用于带行为的类struct Rectangle { double width; double height;}; // 没有成员函数,用 struct
class BankAccount {private: double balance_;public: void deposit(double amount); double balance() const;}; // 有行为,用 class19.3 访问控制详解
Section titled “19.3 访问控制详解”三种访问级别
Section titled “三种访问级别”#include <string>#include <iostream>
class Widget {public: // 公开接口:任何代码都可以访问 std::string name() const { return name_; } void set_name(const std::string& n) { name_ = n; }
protected: // 受保护:此类及其派生类可访问 void reset() { name_ = ""; }
private: // 私有:仅此类可访问 int cache_size_; void clear_cache() { cache_size_ = 0; }
std::string name_;};
// 外部代码Widget w;w.set_name("MyWidget"); // OK,公开接口// w.reset(); // ❌ 派生类才能调用// w.clear_cache(); // ❌ 私有成员,类外不能访问Python 对比
Section titled “Python 对比”# Python 没有真正的访问控制class Widget: def __init__(self): self.public_attr = 1 self._protected_attr = 2 # 约定,不保证 self.__private_attr = 3 # 名称改写,但仍可访问
w = Widget()print(w.__private_attr) # ❌ AttributeErrorprint(w._Widget__private_attr) # ✅ 可以访问!访问控制的应用
Section titled “访问控制的应用”#include <vector>#include <string>#include <iostream>
class Stack {public: // 公开接口 bool empty() const { return items_.empty(); } std::size_t size() const { return items_.size(); } void push(int value) { items_.push_back(value); } int top() const { return items_.back(); } void pop() { items_.pop_back(); }
private: // 私有实现细节 std::vector<int> items_; // 存储方式对外不可见
// 可以添加辅助函数(私有) bool is_full() const { return items_.size() >= max_size_; } static constexpr std::size_t max_size_ = 1000;};
int main() { Stack s; s.push(1); s.push(2); s.push(3);
while (!s.empty()) { std::cout << s.top() << " "; s.pop(); } std::cout << "\n";
return 0;}19.4 成员函数详解
Section titled “19.4 成员函数详解”成员函数的定义位置
Section titled “成员函数的定义位置”#include <string>
class Person {public: // 在类内定义(隐式 inline) std::string greet() const { return "Hello, I'm " + name_; }
// 在类内声明,类外定义 void set_name(const std::string& name); std::string get_name() const;
private: std::string name_;};
// 在类外定义(需要用 ClassName:: 限定)inline void Person::set_name(const std::string& name) { name_ = name;}
inline std::string Person::get_name() const { return name_;}const 成员函数
Section titled “const 成员函数”#include <string>#include <iostream>
class TextBox {private: std::string text_; mutable int access_count_ = 0; // 可在 const 函数中修改
public: void set_text(const std::string& t) { text_ = t; }
// const 成员函数 - 承诺不修改对象状态 std::string get_text() const { ++access_count_; // mutable 可以修改 return text_; }
// 非 const 函数 - 可以修改对象 void append_text(const std::string& t) { text_ += t; }
// 两个重载:const 和非 const char operator[](std::size_t index) const { return text_[index]; }
char& operator[](std::size_t index) { return text_[index]; }};
int main() { TextBox tb; tb.set_text("Hello");
const TextBox& ctb = tb; // const 引用
// ctb.append_text(" World"); // ❌ 不能调用非 const 函数 std::cout << ctb.get_text() << std::endl; // OK,const 函数
// 根据对象是 const 调用不同重载 char c1 = tb[0]; // 调用非 const 版本 char c2 = ctb[0]; // 调用 const 版本
return 0;}this 指针
Section titled “this 指针”#include <string>#include <iostream>
class Counter {private: int value_ = 0;
public: Counter() = default;
// this 是指向当前对象的指针 Counter& increment() { ++value_; return *this; // 返回引用支持链式调用 }
Counter& decrement() { --value_; return *this; }
Counter& reset() { value_ = 0; return *this; }
int value() const { return value_; }};
int main() { Counter cnt;
// 链式调用(类似 Python 的方法链) cnt.increment().increment().decrement().reset();
std::cout << "Value: " << cnt.value() << std::endl; // 0
return 0;}返回引用 vs 返回值
Section titled “返回引用 vs 返回值”#include <string>#include <iostream>
class StringBuilder {private: std::string buffer_;
public: // 返回引用 - 支持链式调用 StringBuilder& append(const std::string& s) { buffer_ += s; return *this; }
StringBuilder& append_line(const std::string& s) { buffer_ += s + "\n"; return *this; }
StringBuilder& clear() { buffer_.clear(); return *this; }
// 返回值(拷贝)- 用于最终结果 std::string build() const { return buffer_; }
// 获取字符串引用(const) const std::string& str() const { return buffer_; }};
int main() { StringBuilder sb;
sb.append("Hello") .append_line(" World") .append("!");
std::cout << sb.build() << std::endl; std::cout << sb.str() << std::endl; // 相同结果
return 0;}19.5 类设计原则
Section titled “19.5 类设计原则”封装的重要性
Section titled “封装的重要性”#include <vector>#include <iostream>#include <stdexcept>
// ❌ 坏的设计:直接暴露内部结构class BadStack {public: std::vector<int> items; // 公开!任何人都能修改};
// ✅ 好的设计:隐藏实现细节class GoodStack {private: std::vector<int> items_;
public: bool empty() const { return items_.empty(); } std::size_t size() const { return items_.size(); } void push(int value) { items_.push_back(value); } int top() const { if (items_.empty()) throw std::runtime_error("Empty stack"); return items_.back(); } void pop() { if (items_.empty()) throw std::runtime_error("Empty stack"); items_.pop_back(); }};
int main() { // BadStack 可以被随意破坏 BadStack bs; bs.items.push_back(1); bs.items.clear(); // 绕过了 stack 的语义
// GoodStack 保证正确的行为 GoodStack gs; gs.push(1); gs.push(2); std::cout << "Top: " << gs.top() << "\n"; // 2
return 0;}#include <string>#include <iostream>
// 清晰的公开接口class File {public: // 构造函数 explicit File(const std::string& path);
// 禁止拷贝 File(const File&) = delete; File& operator=(const File&) = delete;
// 移动语义(C++11) File(File&& other) noexcept; File& operator=(File&& other) noexcept;
// 公开接口 bool open(); void close(); std::size_t read(char* buffer, std::size_t size); bool write(const char* data, std::size_t size);
// 查询 bool is_open() const { return is_open_; } std::size_t size() const { return size_; }
// 析构函数 ~File();
private: // 私有实现细节 std::string path_; void* handle_ = nullptr; // 平台相关句柄 std::size_t size_ = 0; bool is_open_ = false;
void open_platform_impl(); void close_platform_impl();};
// 合理的设计:公开接口小而精确const 正确性
Section titled “const 正确性”#include <string>#include <iostream>
class Database {private: std::string connection_string_; mutable bool cache_valid_ = false; // mutable 用于缓存 mutable std::string cached_result_;
public: // 查询函数应该是 const std::string query(const std::string& sql) const { if (!cache_valid_) { // 执行查询 cached_result_ = execute_query(sql); cache_valid_ = true; } return cached_result_; }
// 修改状态的函数不能是 const void reset() { cache_valid_ = false; cached_result_.clear(); }
// 正确:const 对象只能调用 const 成员函数 // const Database& db = ...; // db.query(...); // OK // db.reset(); // ❌ 编译错误
private: std::string execute_query(const std::string& sql) const;};
int main() { Database db;
const Database& cdb = db; // const 引用
std::string result = cdb.query("SELECT 1"); // OK // cdb.reset(); // ❌ 错误:不能对 const 对象调用非 const 函数
return 0;}19.6 完整示例:有理数类
Section titled “19.6 完整示例:有理数类”#include <iostream>#include <stdexcept>#include <string>
class Rational {private: int numerator_; int denominator_;
// 化简分数 void normalize() { if (denominator_ < 0) { numerator_ = -numerator_; denominator_ = -denominator_; } int g = gcd(numerator_, denominator_); if (g != 0) { numerator_ /= g; denominator_ /= g; } }
static int gcd(int a, int b) { a = std::abs(a); b = std::abs(b); while (b != 0) { int temp = b; b = a % b; a = temp; } return a; }
public: // 构造函数 Rational() : numerator_(0), denominator_(1) {} Rational(int num) : numerator_(num), denominator_(1) {} Rational(int num, int denom) : numerator_(num), denominator_(denom) { if (denom == 0) { throw std::invalid_argument("Denominator cannot be zero"); } normalize(); }
// 访问函数 int numerator() const { return numerator_; } int denominator() const { return denominator_; }
// 算术运算(返回新对象) Rational operator+(const Rational& other) const { int num = numerator_ * other.denominator_ + other.numerator_ * denominator_; int denom = denominator_ * other.denominator_; return Rational(num, denom); }
Rational operator-(const Rational& other) const { int num = numerator_ * other.denominator_ - other.numerator_ * denominator_; int denom = denominator_ * other.denominator_; return Rational(num, denom); }
Rational operator*(const Rational& other) const { return Rational(numerator_ * other.numerator_, denominator_ * other.denominator_); }
Rational operator/(const Rational& other) const { if (other.numerator_ == 0) { throw std::invalid_argument("Division by zero"); } return Rational(numerator_ * other.denominator_, denominator_ * other.numerator_); }
// 比较运算 bool operator==(const Rational& other) const { return numerator_ == other.numerator_ && denominator_ == other.denominator_; }
bool operator<(const Rational& other) const { return numerator_ * other.denominator_ < other.numerator_ * denominator_; }
// 字符串表示 std::string to_string() const { if (denominator_ == 1) { return std::to_string(numerator_); } return std::to_string(numerator_) + "/" + std::to_string(denominator_); }
// 输出流 friend std::ostream& operator<<(std::ostream& os, const Rational& r) { os << r.to_string(); return os; }};
int main() { Rational a(1, 2); // 1/2 Rational b(1, 3); // 1/3
std::cout << "a = " << a << "\n"; std::cout << "b = " << b << "\n"; std::cout << "a + b = " << (a + b) << "\n"; std::cout << "a - b = " << (a - b) << "\n"; std::cout << "a * b = " << (a * b) << "\n"; std::cout << "a / b = " << (a / b) << "\n";
std::cout << "\nComparison:\n"; std::cout << "a == a: " << std::boolalpha << (a == a) << "\n"; std::cout << "a < b: " << (a < b) << "\n";
Rational c(4, 8); // 化简为 1/2 std::cout << "\nc = 4/8 = " << c << "\n"; std::cout << "a == c: " << (a == c) << "\n"; // true
return 0;}19.7 章节总结
Section titled “19.7 章节总结”| 概念 | 说明 |
|---|---|
public | 任何代码可访问 |
protected | 类和派生类可访问 |
private | 仅此类可访问(默认) |
struct vs class | struct 默认 public,class 默认 private |
const 成员函数 | 承诺不修改对象状态 |
this | 指向当前对象的指针 |
返回 *this | 支持链式调用 |
mutable | 允许在 const 函数中修改 |
设计原则:
- 公开接口小而精确
- 私有实现细节隐藏
- const 正确性很重要
- 返回引用支持链式调用
下章预告:ch20 学习构造函数和析构函数,掌握对象初始化和清理。