输入输出流重载说明:std::ostream operator<<(std::ostream os, const Vector v)

首先这是一个函数,函数名字是operator<<,函数的输出类型是std::ostream&,输入类型是std::ostream& const Vector& v相当于(std::cout << v)的重载后的输出可以是std::cout以便格式化输出Vector类型之后还能够继续链式输出其他内容。

// 输入输出流重载
#include <iostream>
struct Vector {
double x, y;
};
// 输出流重载
std::ostream& operator<<(std::ostream& os, const Vector& v) {
os << "(" << v.x << ", " << v.y << ")";
return os;
}
// 输入流重载
std::istream& operator>>(std::istream& is, Vector& v) {
is >> v.x >> v.y;
return is;
}
int main() {
Vector v1, v2;
// 输入
std::cout << "Enter Vector 1 (x y): ";
std::cin >> v1; // 例如输入: 1.0 2.0
std::cout << "Enter Vector 2 (x y): ";
std::cin >> v2; // 例如输入: 3.0 4.0
// 输出
std::cout << "Vector 1: " << v1 << std::endl; // 输出: Vector 1: (1.0, 2.0)
std::cout << "Vector 2: " << v2 << std::endl; // 输出: Vector 2: (3.0, 4.0)
return 0;
}
2.2.3 函数运算符重载
class Adder {
public:
int operator()(int a, int b) {
return a + b;
}
};
Adder add;
int result = add(3, 4); // 看起来像函数调用,实际是调用 operator()
// 匿名函数对象
std::cout << Adder()(3, 4) << std::endl;

2.3 继承

概念:继承允许我们创建一个新类(派生类),这个新类会继承一个基类的属性行为,实现代码的重用和类之间的层次关系。

核心思想:提取出一类物品的公共属性和行为。比如猫狗鸟等都属于动物,动物就可以作为父类。

继承是多态的基础。

子类可以对父类的成员函数进行重写。虚函数的重写是为了实现多态。

2.3.1 语法格式

// class Manager : public Employee {
#include <iostream>
#include <string>
// 基类:员工
class Employee {
public:
// 基类的构造函数
Employee(std::string name, int id, double salary)
: m_name(name),
m_id(id),
m_salary(salary) {}
// 基类的成员函数
void work() {
std::cout << m_name << " (ID: " << m_id << ") is working." << std::endl;
}
void showInfo() {
std::cout << "Name: " << m_name << ", ID: " << m_id << ", Salary: " << m_salary << std::endl;
}
protected: // protected成员在基类和派生类中都可以访问
std::string m_name;
int m_id;
double m_salary;
};
// 派生类:经理,公有继承自Employee
class Manager : public Employee {
public:
// 派生类的构造函数,需要调用基类的构造函数来初始化基类部分
Manager(std::string name, int id, double salary, double bonus)
: Employee(name, id, salary),
m_bonus(bonus) {} // 初始化列表
// 派生类新增的成员函数
void manageTeam() {
std::cout << m_name << " is managing the team." << std::endl;
}
// 派生类可以重写(覆盖)基类的函数
void showInfo() {
// 先调用基类的showInfo()显示共同信息
Employee::showInfo(); // 使用作用域解析符调用基类版本
// 再显示派生类特有的信息
std::cout << "Bonus: " << m_bonus << std::endl;
}
private:
// 派生类新增的成员变量
double m_bonus;
};
int main() {
Employee emp("Alice", 1001, 8000.0);
emp.work(); // Alice (ID: 1001) is working.
emp.showInfo(); // Name: Alice, ID: 1001, Salary: 8000
std::cout << "---------------------" << std::endl;
Manager mgr("Bob", 2001, 15000.0, 5000.0);
mgr.work(); // Bob (ID: 2001) is working. (继承自Employee)
mgr.manageTeam(); // Bob is managing the team. (Manager自己的)
mgr.showInfo(); // 调用的是Manager重写后的版本
// Name: Bob, ID: 2001, Salary: 15000
// Bonus: 5000
return 0;
}

2.3.2 继承方式

公有,保护,私有三种方式。继承方式决定了基类中的成员派生类中的访问权限。继承方式是为了限制“外部”对“基类部分”的访问,而不是限制派生类内部对基类成员的访问。总之,派生类对基类的访问权限取决于基类和继承方式的最小权限。只有两个都是public时外部才能访问。

无论哪种继承方式,基类的private成员永远无法被派生类直接访问。它们虽然被继承了(存在于派生类对象中),但对派生类来说是“不可见”的。派生类只能通过基类提供的publicprotected接口来间接访问它们。

2.3.3 继承中的构造/析构函数

构造函数和析构函数不能被继承,但是在创建派生类对象时,基类的构造函数会自动被调用。

构造函数的调用顺序:先调用基类的构造函数,再调用派生类自己的构造函数。

// Manager的构造函数
Manager(std::string name, int id, double salary, double bonus)
: Employee(name, id, salary), // 在初始化列表中调用基类构造函数
m_bonus(bonus) { // 初始化派生类自己的成员
// 函数体
}

析构函数的调用顺序相反。

2.3.4 继承的内存布局

派生类对象包含了基类的所有非静态成员变量,以及派生类自己新增的非静态成员变量。这些成员在内存中通常是连续存放的,基类的部分在前,派生类的部分在后。

成员函数(包括虚函数)并不存储在每个对象中。它们存储在代码段。每个对象中只存储一个指向虚函数表的指针(如果类有虚函数),通过这个指针来找到正确的函数版本。

2.3.5 继承中的静态成员

无论继承出多少个派生类,整个继承体系中只有一个静态成员的实例

  • 静态成员变量:被所有基类和派生类的对象共享。
  • 静态成员函数:没有this指针,只能访问静态成员。它同样被继承,但无法被重写为虚函数(因为虚函数依赖于this指针和虚表)。

静态成员函数可以通过基类或者派生类的作用域来访问。