文章目录
- 类
- 友元
- 关键点说明:
- 运行输出:
- 友元类的例子
- 使用友元的情况
- 1. **需要外部函数直接操作类的私有成员**
- 示例场景:打印类的数据
- 2. **两个类之间需要紧密协作**
- 示例场景:管理类和数据类
- 3. **操作符重载需要访问私有成员**
- 示例场景:重载输出操作符
- 4. **测试或调试需要访问内部状态**
- 示例场景:调试工具
- 注意事项和使用建议
- 总结
- 继承
- 输出:
- 关键点解释
- 1. **基类(Animal)**
- 2. **派生类(Dog)**
- 3. **继承类型**
- 4. **方法重写**
- using type = int;
- 类型别名基本用法
- 输出:
- 特点:
- 与虚函数结合的示例
- 输出:
- 代码中的关键点
- `using` vs `typedef`
- 优势:
- 使用场景
类
下面是一个简单的C++面向对象编程示例,展示类、对象和基本成员的使用:
#include <iostream>
#include <string>
#include <windows.h> using namespace std;// 定义一个简单的类
class Person {
private:string name; // 私有成员变量int age;public:// 构造函数Person(string n, int a) {name = n;age = a;}// 成员函数void introduce() {cout << "Hello, my name is " << name << " and I am " << age << " years old." << endl;}
};int main() {SetConsoleOutputCP(CP_UTF8); // 设置控制台为 UTF-8 编码// 创建对象Person person1("Alice", 25);// 调用成员函数person1.introduce();return 0;
}
这个程序展示了C++面向对象的基本特性:
- 类定义 (
class Person
):包含私有数据成员和公有成员函数 - 封装:name 和 age 是私有的,只能通过类的成员函数访问
- 构造函数:用于初始化对象
- 对象创建:使用类创建具体实例
- 成员函数:定义对象的行为
运行输出:
Hello, my name is Alice and I am 25 years old.
友元
下面是一个展示C++中友元(friend)功能的简单示例。友元允许特定的函数或类访问另一个类的私有成员:
#include <iostream>
#include <string>using namespace std;class Person {
private:string name;int age;public:// 构造函数Person(string n, int a) {name = n;age = a;}// 声明友元函数friend void showInfo(Person p);
};// 友元函数定义,可以访问Person的私有成员
void showInfo(Person p) {cout << "Name: " << p.name << ", Age: " << p.age << endl;
}int main() {// 创建对象Person person1("Bob", 30);// 调用友元函数showInfo(person1);return 0;
}
关键点说明:
- 友元声明:在
Person
类中通过friend void showInfo(Person p);
声明showInfo
为友元函数。 - 访问权限:尽管
name
和age
是私有的,showInfo
函数仍能直接访问它们,因为它是友元。 - 使用场景:友元通常用于需要打破封装但又必须访问私有成员的情况。
运行输出:
Name: Bob, Age: 30
友元类的例子
如果你想要看友元类的用法,这里是一个扩展示例:
#include <iostream>
#include <string>using namespace std;class Person {
private:string name;int age;public:Person(string n, int a) : name(n), age(a) {}// 声明另一个类为友元friend class InfoPrinter;
};// 友元类
class InfoPrinter {
public:void display(Person p) {cout << "Name: " << p.name << ", Age: " << p.age << endl;}
};int main() {Person person1("Alice", 25);InfoPrinter printer;printer.display(person1);return 0;
}
在这个例子中,InfoPrinter
类是 Person
的友元类,因此它可以访问 Person
的私有成员。输出与之前相同,但展示了类级别的友元关系。
使用友元的情况
1. 需要外部函数直接操作类的私有成员
当一个独立的函数需要访问类的私有数据,且不适合作为类的成员函数时,可以使用友元函数。
示例场景:打印类的数据
#include <iostream>
#include <string>using namespace std;class Box {
private:double width;double height;public:Box(double w, double h) : width(w), height(h) {}// 声明友元函数friend void printArea(Box b);
};// 友元函数直接访问私有成员
void printArea(Box b) {double area = b.width * b.height;cout << "Area: " << area << endl;
}int main() {Box box(5.0, 3.0);printArea(box);return 0;
}
使用情况:printArea
不属于 Box
类,但需要计算和访问其私有成员。将其声明为友元避免了添加额外的公有 getter 函数。
2. 两个类之间需要紧密协作
当两个类相互依赖,并且一个类需要访问另一个类的私有成员时,可以将其中一个类声明为另一个类的友元。
示例场景:管理类和数据类
#include <iostream>
#include <string>using namespace std;class Employee {
private:string name;double salary;public:Employee(string n, double s) : name(n), salary(s) {}// 声明 Manager 为友元类friend class Manager;
};class Manager {
public:void adjustSalary(Employee& e, double newSalary) {e.salary = newSalary; // 直接访问私有成员cout << e.name << "'s new salary: " << e.salary << endl;}
};int main() {Employee emp("John", 50000);Manager mgr;mgr.adjustSalary(emp, 60000);return 0;
}
使用情况:Manager
需要直接修改 Employee
的私有成员 salary
,通过友元关系实现紧密协作。
3. 操作符重载需要访问私有成员
在重载操作符(如 +
, <<
)时,如果操作符函数不是类的成员函数,通常需要访问类的私有数据,这时可以声明为友元。
示例场景:重载输出操作符
#include <iostream>
#include <string>using namespace std;class Point {
private:int x;int y;public:Point(int xVal, int yVal) : x(xVal), y(yVal) {}// 声明友元函数重载 <<friend ostream& operator<<(ostream& os, const Point& p);
};// 友元函数实现
ostream& operator<<(ostream& os, const Point& p) {os << "(" << p.x << ", " << p.y << ")";return os;
}int main() {Point p(3, 4);cout << p << endl;return 0;
}
使用情况:<<
操作符需要访问 Point
的私有成员 x
和 y
,通过友元实现简洁的输出。
4. 测试或调试需要访问内部状态
在开发过程中,为了测试类的私有成员状态,可以临时使用友元函数或友元类。
示例场景:调试工具
#include <iostream>using namespace std;class System {
private:int status;public:System(int s) : status(s) {}// 声明友元函数用于调试friend void debugStatus(System sys);
};void debugStatus(System sys) {cout << "System status: " << sys.status << endl;
}int main() {System sys(42);debugStatus(sys);return 0;
}
使用情况:debugStatus
用于检查 System
的内部状态,便于调试。
注意事项和使用建议
- 谨慎使用:友元打破了封装性,可能导致代码维护困难。通常优先考虑使用公有接口(getter/setter)或成员函数。
- 替代方案:如果只是简单的数据访问,可以用公有方法代替;如果涉及复杂逻辑,可以考虑继承或组合。
- 典型应用:友元在操作符重载、紧密相关的类(如容器和迭代器)以及特殊工具函数中较为常见。
总结
友元的使用情况主要集中在:
- 外部函数需要访问私有数据(如打印、计算)
- 类间紧密协作(如管理类和数据类)
- 操作符重载(如
<<
,+
) - 测试和调试需求
继承
下面是一个展示C++继承(Inheritance)基本概念的简单示例,包括基类、派生类和访问控制:
父类继承下来,会继承三样东西:
1.成员变量
2.成员函数
3.类型定义
#include <iostream>
#include <string>using namespace std;// 基类
class Animal {
protected: // protected成员可被派生类访问string name;int age;public:Animal(string n, int a) : name(n), age(a) {}void eat() {cout << name << " is eating." << endl;}
};// 派生类(公有继承)
class Dog : public Animal {
private:string breed;public:// 构造函数,调用基类构造函数Dog(string n, int a, string b) : Animal(n, a), breed(b) {}void bark() {cout << name << " (a " << breed << ") is barking!" << endl;}// 重写基类方法void eat() {cout << name << " (a " << breed << ") is eating dog food." << endl;}
};int main() {// 创建基类对象Animal animal("Generic", 5);animal.eat();// 创建派生类对象Dog dog("Max", 3, "Golden Retriever");dog.eat(); // 调用重写后的方法dog.bark(); // 调用派生类特有方法return 0;
}
输出:
Generic is eating.
Max (a Golden Retriever) is eating dog food.
Max (a Golden Retriever) is barking!
关键点解释
1. 基类(Animal)
- 定义了通用的属性(
name
,age
)和行为(eat()
)。 - 使用
protected
访问修饰符,使派生类可以访问这些成员。
2. 派生类(Dog)
- 使用
public
继承方式从Animal
继承。 - 继承了基类的
name
和age
,并添加了自己的属性breed
。 - 通过构造函数初始化列表调用基类构造函数:
Animal(n, a)
。
3. 继承类型
- 公有继承(public):基类的公有成员在派生类中仍为公有,受保护成员仍为受保护。
- 私有继承(private):基类的公有和受保护成员在派生类中变为私有。
- 受保护继承(protected):基类的公有成员变为受保护,受保护成员保持不变。
4. 方法重写
Dog
类重写了基类的eat()
方法,提供了更具体的实现。- 基类的原始
eat()
方法被覆盖,但仍可通过作用域解析运算符(如Animal::eat()
)调用。
using type = int;
你提到的 using type = int;
是C++11引入的类型别名(Type Alias)语法,用于为现有类型定义一个新的名称。它类似于 typedef
,但更灵活且可读性更好。下面我将解释它的用法,并结合虚函数和多态的上下文提供一个相关示例。
类型别名基本用法
#include <iostream>using namespace std;// 类型别名
using type = int; // "type" 现在是 "int" 的别名int main() {type number = 42; // 等价于 int number = 42;cout << "Number: " << number << endl;return 0;
}
输出:
Number: 42
特点:
using type = int;
定义type
为int
的别名。- 与
typedef int type;
等价,但using
语法更直观,且支持模板。
与虚函数结合的示例
假设我们在一个多态场景中使用类型别名来简化代码:
#include <iostream>
#include <string>
#include <vector>using namespace std;// 类型别名
using Distance = double; // 为 double 定义别名// 基类
class Vehicle {
protected:string name;public:Vehicle(string n) : name(n) {}// 虚函数virtual Distance calculateFuelEfficiency() const {return 0.0; // 默认实现}virtual ~Vehicle() {}
};// 派生类1
class Car : public Vehicle {
private:Distance miles;Distance gallons;public:Car(string n, Distance m, Distance g) : Vehicle(n), miles(m), gallons(g) {}Distance calculateFuelEfficiency() const override {return miles / gallons; // 计算每加仑英里数}
};// 派生类2
class Bike : public Vehicle {
private:Distance miles;Distance liters;public:Bike(string n, Distance m, Distance l) : Vehicle(n), miles(m), liters(l) {}Distance calculateFuelEfficiency() const override {return miles / liters; // 计算每升英里数}
};int main() {vector<Vehicle*> vehicles;vehicles.push_back(new Car("Toyota", 300.0, 10.0));vehicles.push_back(new Bike("Harley", 120.0, 2.0));for (const auto* v : vehicles) {Distance efficiency = v->calculateFuelEfficiency();cout << "Fuel efficiency: " << efficiency << " miles per unit" << endl;}for (auto* v : vehicles) {delete v;}return 0;
}
输出:
Fuel efficiency: 30 miles per unit
Fuel efficiency: 60 miles per unit
代码中的关键点
-
类型别名(
using Distance = double;
):- 将
double
定义为Distance
,提高了代码的可读性,表示这是一个距离或效率相关的类型。 - 在类中统一使用
Distance
,如果将来需要更改类型(如改为float
),只需改别名定义。
- 将
-
虚函数(
calculateFuelEfficiency
):- 基类提供默认实现,返回类型为
Distance
。 - 派生类
Car
和Bike
重写该函数,计算不同的燃料效率。
- 基类提供默认实现,返回类型为
-
运行时多态:
- 通过
Vehicle*
指针调用calculateFuelEfficiency()
,运行时根据对象类型执行对应的实现。
- 通过
using
vs typedef
typedef
(C++98):typedef double Distance; // 传统方式
using
(C++11):using Distance = double; // 现代方式
优势:
using
支持模板别名,而typedef
不支持:template<typename T> using Ptr = T*; // 模板别名 Ptr<int> p = nullptr; // int* p
using
语法更直观,类似于赋值。
使用场景
- 提高可读性:
- 用
Distance
表示距离相关的变量,而不是直接用double
。
- 用
- 类型统一:
- 在大型项目中,统一类型别名便于维护。
- 模板编程:
using
在模板中更灵活,如定义容器别名:template<typename T> using Vec = std::vector<T>; Vec<int> v; // 等价于 std::vector<int>