Skip to content

Ch 19: 类基础

  • 深入理解 Python 类和 C++ 类的本质区别
  • 掌握类的基本结构、访问控制和成员函数
  • 理解 this 指针的用法和链式调用
  • 学会设计简洁清晰的类接口
  • 理解 const 成员函数的重要性
# 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 的类非常灵活
class DynamicClass:
pass
# 可以在运行时添加属性
obj = DynamicClass()
obj.new_attr = "value"
# 多继承
class A: pass
class B: pass
class 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()) # Derived
# Python - 依赖约定
class Account:
def __init__(self, balance):
self.balance = balance # 公开,谁都能改
self._transactions = [] # 约定受保护
self.__pin = 1234 # 名称改写,但仍可访问(_Account__pin)
# 但Python没有真正的私有
print(account._Account__pin) # 可以访问
#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 - 默认 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;
}; // 有行为,用 class
#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 没有真正的访问控制
class Widget:
def __init__(self):
self.public_attr = 1
self._protected_attr = 2 # 约定,不保证
self.__private_attr = 3 # 名称改写,但仍可访问
w = Widget()
print(w.__private_attr) # ❌ AttributeError
print(w._Widget__private_attr) # ✅ 可以访问!
#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;
}
#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_;
}
#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;
}
#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;
}
#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;
}
#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();
};
// 合理的设计:公开接口小而精确
#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;
}
#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;
}
概念说明
public任何代码可访问
protected类和派生类可访问
private仅此类可访问(默认)
struct vs classstruct 默认 public,class 默认 private
const 成员函数承诺不修改对象状态
this指向当前对象的指针
返回 *this支持链式调用
mutable允许在 const 函数中修改

设计原则:

  • 公开接口小而精确
  • 私有实现细节隐藏
  • const 正确性很重要
  • 返回引用支持链式调用

下章预告:ch20 学习构造函数和析构函数,掌握对象初始化和清理。