关联关系
- 1.关联关系
- 1.1单向关联
- 1.2双向关联
- 1.3自关联
- 2.聚合关系
- 3.组合关系
- 4.依赖关系
- 5.继承关系
- 6 实现关系
1.关联关系
关联关系是面向对象编程中的一个概念,用于描述两个类之间的联系。在关联关系中,一个类的对象与另一个类的对象之间存在某种关联,但它们的关系不如组合关系那样密切。这种关联可以是单向的,也可以是双向的。
在关联关系中,一个类的对象可以作为另一个类的成员变量,或者作为方法的参数或返回值等形式出现。这种关系可以用来描述现实世界中的各种情况,例如学生和课程之间的关系、订单和产品之间的关系等。
关联关系的特点包括:
- 双向关系:两个类之间的关联可以是双向的,即彼此互相关联。
- 多重性:关联关系中的对象之间可能存在一对一、一对多、多对一或多对多等不同的关系。
- 独立性:关联关系中的两个对象的生命周期通常是独立的,它们之间的存在并不依赖于彼此。
关联关系在代码实现中通常通过成员变量、方法参数或返回值来体现,以反映出两个类之间的关联性。在使用关联关系时,需要注意避免过度耦合,以提高代码的灵活性和可维护性。
1.1单向关联
在下面的示例中,Order 类中使用了指针 const Customer* customer 来表示订单和客户之间的单向关联关系。在 main 函数中创建订单对象时,将客户对象的地址传入订单对象的构造函数中,从而建立了单向关联关系。这样,订单对象可以通过指针访问关联的客户对象的信息,但客户对象无法直接通过订单对象访问关联的订单信息,从而形成了单向关联关系。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 定义客户类
class Customer {
private:
int customerId;
string customerName;
public:
Customer(int id, const string& name) : customerId(id), customerName(name) {}
int getCustomerId() const {
return customerId;
}
const string& getCustomerName() const {
return customerName;
}
};
// 定义订单类
class Order {
private:
int orderId;
string orderDate;
const Customer* customer; // 使用指针表示关联关系
public:
Order(int id, const string& date, const Customer* cust) : orderId(id), orderDate(date), customer(cust) {}
int getOrderId() const {
return orderId;
}
const string& getOrderDate() const {
return orderDate;
}
const Customer* getCustomer() const {
return customer;
}
};
int main() {
// 创建客户对象
Customer customer1(1, "John Doe");
// 创建订单对象,并传入客户对象的地址作为关联
Order order1(1001, "2024-03-30", &customer1);
// 获取订单信息以及关联的客户信息
cout << "Order ID: " << order1.getOrderId() << endl;
cout << "Order Date: " << order1.getOrderDate() << endl;
cout << "Customer ID: " << order1.getCustomer()->getCustomerId() << endl;
cout << "Customer Name: " << order1.getCustomer()->getCustomerName() << endl;
return 0;
}
1.2双向关联
双向关联关系意味着两个类之间的关联是双向的,每个类都能够直接访问关联的对象。在 C++ 中实现双向关联关系时,可以在每个类中使用指针或引用来表示另一个类的关联。在下面的示例中,Customer 类和 Order 类分别存储了彼此的指针,并提供了方法来访问关联的对象。在创建订单对象时,将订单添加到客户的订单列表中,从而建立了双向关联关系。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 前置声明,避免互相引用时出错
class Order;
// 定义客户类
class Customer {
private:
int customerId;
string customerName;
vector<Order*> orders; // 用于存储关联的订单指针
public:
Customer(int id, const string& name) : customerId(id), customerName(name) {}
int getCustomerId() const {
return customerId;
}
const string& getCustomerName() const {
return customerName;
}
// 添加订单到客户的订单列表中
void addOrder(Order* order) {
orders.push_back(order);
}
// 获取客户的所有订单
const vector<Order*>& getOrders() const {
return orders;
}
};
// 定义订单类
class Order {
private:
int orderId;
string orderDate;
Customer* customer; // 用于存储关联的客户指针
public:
Order(int id, const string& date, Customer* cust) : orderId(id), orderDate(date), customer(cust) {
// 在订单构造函数中,将订单添加到客户的订单列表中
customer->addOrder(this);
}
int getOrderId() const {
return orderId;
}
const string& getOrderDate() const {
return orderDate;
}
// 获取订单关联的客户
Customer* getCustomer() const {
return customer;
}
};
int main() {
// 创建客户对象和订单对象
Customer customer1(1, "John Doe");
Order order1(1001, "2024-03-30", &customer1);
// 获取订单关联的客户以及客户关联的订单
cout << "Order ID: " << order1.getOrderId() << endl;
cout << "Order Date: " << order1.getOrderDate() << endl;
cout << "Customer ID: " << order1.getCustomer()->getCustomerId() << endl;
cout << "Customer Name: " << order1.getCustomer()->getCustomerName() << endl;
cout << "Customer's Orders:" << endl;
const vector<Order*>& customer1Orders = customer1.getOrders();
for (const auto& order : customer1Orders) {
cout << " - Order ID: " << order->getOrderId() << ", Order Date: " << order->getOrderDate() << endl;
}
return 0;
}
1.3自关联
自关联是指一个类与自身建立关联关系。在 C++ 中,可以通过在类中使用指针或引用来实现自关联。在下面的示例中,Node 类表示一个简单的链表节点,包含一个整数数据成员和一个指向下一个节点的指针。通过在节点类中使用指针来指向下一个节点,实现了节点之间的自关联关系。在 main 函数中,创建了三个节点对象,并通过调用 setNext 方法建立了节点之间的关联关系。然后,通过遍历链表输出节点数据,最后释放了动态分配的内存。
#include <iostream>
#include <string>
using namespace std;
// 定义节点类
class Node {
private:
int data;
Node* next; // 指向下一个节点的指针
public:
Node(int val) : data(val), next(nullptr) {}
// 设置下一个节点
void setNext(Node* node) {
next = node;
}
// 获取下一个节点
Node* getNext() const {
return next;
}
// 获取节点数据
int getData() const {
return data;
}
};
int main() {
// 创建节点对象
Node* node1 = new Node(1);
Node* node2 = new Node(2);
Node* node3 = new Node(3);
// 建立节点之间的关联关系
node1->setNext(node2);
node2->setNext(node3);
// 遍历节点链表
Node* current = node1;
while (current != nullptr) {
cout << "Node data: " << current->getData() << endl;
current = current->getNext();
}
// 释放动态分配的内存
delete node1;
delete node2;
delete node3;
return 0;
}
2.聚合关系
聚合关系是关联关系的一种,是强关联关系,是整体和部分之间的关系。在聚合关系中,一个类作为另一个类的成员,并且这个成员对象的生命周期可以独立于包含它的类。在 C++ 中,聚合关系通常通过在类中创建成员对象或指针来实现。聚合关系的特点是,成员可以脱离于整体对象独立存在,比如学校与老师的关系,学校包含老师,但是就算学校没了,老师依然可以存在。
在下面的示例中,Teacher 类表示老师,School 类表示学校。在 School 类中,使用了一个 vector 来存储学校包含的老师对象的指针,这体现了学校和老师之间的聚合关系。通过 addTeacher 方法可以向学校对象中添加老师。通过 getTeacherNames 方法可以获取学校所有老师的姓名。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// 老师类
class Teacher {
private:
string name;
public:
Teacher(const string& n) : name(n) {}
const string& getName() const {
return name;
}
};
// 学校类
class School {
private:
string name;
vector<Teacher*> teachers; // 聚合关系,学校包含了多个老师对象的指针
public:
School(const string& n) : name(n) {}
// 添加老师
void addTeacher(Teacher* teacher) {
teachers.push_back(teacher);
}
// 获取学校所有老师的姓名
vector<string> getTeacherNames() const {
vector<string> teacherNames;
for (const auto& teacher : teachers) {
teacherNames.push_back(teacher->getName());
}
return teacherNames;
}
// 获取学校名称
const string& getName() const {
return name;
}
};
int main() {
// 创建老师对象
Teacher mrSmith("Mr. Smith");
Teacher msJohnson("Ms. Johnson");
// 创建学校对象
School highSchool("High School");
// 将老师添加到学校中
highSchool.addTeacher(&mrSmith);
highSchool.addTeacher(&msJohnson);
// 输出学校名称
cout << "School name: " << highSchool.getName() << endl;
// 输出学校所有老师的姓名
cout << "Teachers in " << highSchool.getName() << ":" << endl;
for (const auto& teacherName : highSchool.getTeacherNames()) {
cout << teacherName << endl;
}
return 0;
}
3.组合关系
组合表示类之间的整体与部分的关系,但它是一种更强烈的聚合关系。在组合关系中,整体对象可以控制部分对象的生命周期,一旦整体对象不存在,部分对象也将不存在,部分对象不能脱离整体对象而存在。在组合关系中,一个类包含另一个类的对象,并且包含的对象的生命周期与包含它的类的生命周期相同。在 C++ 中,组合关系通常通过在类中直接创建成员对象来实现。
在下面的示例中,Wheel 类表示轮子,Car 类表示汽车。Car 类包含了四个 Wheel 类的对象作为成员变量,这实现了 Car 类对 Wheel 类的组合关系。在 main 函数中,创建了一个汽车对象 myCar,并输出了汽车的型号以及每个轮子的尺寸。
#include <iostream>
#include <string>
using namespace std;
// 定义轮子类
class Wheel {
private:
int size;
public:
Wheel(int s) : size(s) {}
int getSize() const {
return size;
}
};
// 定义汽车类,其中包含轮子对象
class Car {
private:
string model;
Wheel frontLeftWheel;
Wheel frontRightWheel;
Wheel rearLeftWheel;
Wheel rearRightWheel;
public:
Car(const string& m, int wheelSize) :
model(m),
frontLeftWheel(wheelSize),
frontRightWheel(wheelSize),
rearLeftWheel(wheelSize),
rearRightWheel(wheelSize) {}
const string& getModel() const {
return model;
}
const Wheel& getFrontLeftWheel() const {
return frontLeftWheel;
}
const Wheel& getFrontRightWheel() const {
return frontRightWheel;
}
const Wheel& getRearLeftWheel() const {
return rearLeftWheel;
}
const Wheel& getRearRightWheel() const {
return rearRightWheel;
}
};
int main() {
// 创建汽车对象
Car myCar("Toyota", 18);
// 输出汽车信息及轮子信息
cout << "Car Model: " << myCar.getModel() << endl;
cout << "Front Left Wheel Size: " << myCar.getFrontLeftWheel().getSize() << " inches" << endl;
cout << "Front Right Wheel Size: " << myCar.getFrontRightWheel().getSize() << " inches" << endl;
cout << "Rear Left Wheel Size: " << myCar.getRearLeftWheel().getSize() << " inches" << endl;
cout << "Rear Right Wheel Size: " << myCar.getRearRightWheel().getSize() << " inches" << endl;
return 0;
}
4.依赖关系
依赖关系是一种使用关系,它是对象之间耦合度最弱的一种关联方式,是临时性的关联。在代码中,某个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另一个类(被依赖类)中的某些方法来完成一些职责。在依赖关系中,一个类使用另一个类的对象作为参数或者局部变量,但是它们之间没有拥有关系。依赖关系通常体现在方法参数的传递或局部变量的使用上。
在下面的示例中,Engine 类表示引擎,Car 类表示汽车。在 Car 类的 drive 方法中,使用了 Engine 类的对象作为参数,这体现了 Car 类对 Engine 类的依赖关系。在 main 函数中,创建了一个汽车对象 myCar 和一个引擎对象 myEngine,然后调用了汽车对象的 drive 方法,并将引擎对象作为参数传入。
#include <iostream>
#include <string>
using namespace std;
// 定义引擎类
class Engine {
public:
void start() {
cout << "Engine started." << endl;
}
void stop() {
cout << "Engine stopped." << endl;
}
};
// 定义汽车类,其中使用了引擎对象作为参数
class Car {
private:
string model;
public:
Car(const string& m) : model(m) {}
// 方法中使用了引擎对象作为参数,体现了依赖关系
void drive(Engine& engine) {
cout << "Driving the " << model << " car." << endl;
engine.start();
// Driving logic...
engine.stop();
}
};
int main() {
// 创建汽车对象和引擎对象
Car myCar("Toyota");
Engine myEngine;
// 调用汽车对象的方法,传入引擎对象作为参数
myCar.drive(myEngine);
return 0;
}
5.继承关系
继承关系是耦合度最大的一种,也是我们最常看见的一种,子类基础父类。在关联关系中,一个类和另一个类有联系,但它们之间的关系不如组合关系密切。通常,一个类的对象可以作为另一个类的成员变量,但它们之间的生命周期可以是独立的。继承关系则是一种类与类之间的特殊关系,其中一个类(子类)可以继承另一个类(父类)的属性和方法。
在下面的示例中,Wheel 类表示轮子,Car 类表示汽车,SportsCar 类表示跑车。Car 类中包含了四个 Wheel 对象的指针,这体现了关联关系。而 SportsCar 类继承自 Car 类,这体现了继承关系。在 main 函数中,创建了一个跑车对象 mySportsCar,并输出了跑车的型号、是否有涡轮增压以及每个轮子的尺寸。
#include <iostream>
#include <string>
using namespace std;
// 定义轮子类
class Wheel {
private:
int size;
public:
Wheel(int s) : size(s) {}
int getSize() const {
return size;
}
};
// 定义汽车类,其中关联了轮子对象
class Car {
private:
string model;
Wheel* wheels[4]; // 关联了四个轮子对象的指针
public:
Car(const string& m, int wheelSize) : model(m) {
for (int i = 0; i < 4; ++i) {
wheels[i] = new Wheel(wheelSize);
}
}
~Car() {
for (int i = 0; i < 4; ++i) {
delete wheels[i];
}
}
const string& getModel() const {
return model;
}
const Wheel* getWheel(int index) const {
if (index >= 0 && index < 4) {
return wheels[index];
} else {
return nullptr;
}
}
};
// 定义跑车类,继承自汽车类
class SportsCar : public Car {
private:
bool turbo;
public:
SportsCar(const string& m, int wheelSize, bool t) : Car(m, wheelSize), turbo(t) {}
bool hasTurbo() const {
return turbo;
}
};
int main() {
// 创建跑车对象
SportsCar mySportsCar("Ferrari", 20, true);
// 输出跑车信息及轮子信息
cout << "Sports Car Model: " << mySportsCar.getModel() << endl;
cout << "Has Turbo: " << (mySportsCar.hasTurbo() ? "Yes" : "No") << endl;
for (int i = 0; i < 4; ++i) {
cout << "Wheel " << i + 1 << " Size: " << mySportsCar.getWheel(i)->getSize() << " inches" << endl;
}
return 0;
}
6 实现关系
实现关系(Implementation Relationship)是面向对象编程中描述类与接口之间的关系。在实现关系中,类实现了一个或多个接口,它承诺实现接口中定义的所有抽象方法和属性。
在下面示例中,Transportation 是一个接口类,其中声明了纯虚函数 move(),表示交通工具的移动操作。Car 和 Ship 分别是实现了 Transportation 接口的类,它们都必须提供 move() 方法的具体实现。在 main() 函数中,我们创建了 Car 和 Ship 的对象,并通过这些对象调用了 move() 方法,展示了实现关系的用法。
#include <iostream>
// 定义交通工具接口
class Transportation {
public:
virtual void move() = 0; // 抽象方法,表示交通工具的移动操作
};
// 汽车类实现了交通工具接口
class Car : public Transportation {
public:
void move() override {
std::cout << "Car is moving on the road." << std::endl;
}
};
// 船类实现了交通工具接口
class Ship : public Transportation {
public:
void move() override {
std::cout << "Ship is sailing on the sea." << std::endl;
}
};
int main() {
Car car;
Ship ship;
// 通过实现了接口的对象调用接口方法
car.move();
ship.move();
return 0;
}