引言

在传统的 C++ 面向对象设计中,我们通常使用虚函数实现多态。本文将展示如何使用std::function替代虚函数,并结合移动语义,构建一个更灵活高效的图形计算程序。这种方式不仅能保持多态性,还能提升性能并增加代码灵活性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#include <iostream>
#include <string>
#include <functional>
#include <cmath>
#include <utility>
#include <algorithm>

// 图形基类,使用std::function替代虚函数
class Figure {
public:
// 定义函数类型
using GetNameFunc = std::function<std::string()>;
using GetAreaFunc = std::function<double()>;

// 构造函数,接受函数对象并移动它们
Figure(GetNameFunc nameFunc, GetAreaFunc areaFunc)
: getNameFunc(std::move(nameFunc)),
getAreaFunc(std::move(areaFunc)) {}

// 移动构造函数
Figure(Figure&& other) noexcept
: getNameFunc(std::move(other.getNameFunc)),
getAreaFunc(std::move(other.getAreaFunc)) {}

// 移动赋值运算符
Figure& operator=(Figure&& other) noexcept {
if (this != &other) {
getNameFunc = std::move(other.getNameFunc);
getAreaFunc = std::move(other.getAreaFunc);
}
return *this;
}

// 禁用拷贝操作
Figure(const Figure&) = delete;
Figure& operator=(const Figure&) = delete;

// 接口方法
std::string getName() const { return getNameFunc(); }
double getArea() const { return getAreaFunc(); }

virtual ~Figure() = default;

private:
GetNameFunc getNameFunc;
GetAreaFunc getAreaFunc;
};

// 矩形类
class Rectangle : public Figure {
public:
Rectangle(double len, double wid)
: Figure(
[this]() { return "矩形"; },
[this]() { return _length * _width; }),
_length(len), _width(wid) {}

// 移动构造函数
Rectangle(Rectangle&& other) noexcept
: Figure(std::move(other)),
_length(std::exchange(other._length, 0)),
_width(std::exchange(other._width, 0)) {}

// 移动赋值运算符
Rectangle& operator=(Rectangle&& other) noexcept {
if (this != &other) {
Figure::operator=(std::move(other));
_length = std::exchange(other._length, 0);
_width = std::exchange(other._width, 0);
}
return *this;
}

// 禁用拷贝操作
Rectangle(const Rectangle&) = delete;
Rectangle& operator=(const Rectangle&) = delete;

private:
double _length;
double _width;
};

// 圆形类
class Circle : public Figure {
public:
Circle(double radius)
: Figure(
[this]() { return "圆形"; },
[this]() { return M_PI * _radius * _radius; }),
_radius(radius) {}

// 移动构造函数
Circle(Circle&& other) noexcept
: Figure(std::move(other)),
_radius(std::exchange(other._radius, 0)) {}

// 移动赋值运算符
Circle& operator=(Circle&& other) noexcept {
if (this != &other) {
Figure::operator=(std::move(other));
_radius = std::exchange(other._radius, 0);
}
return *this;
}

// 禁用拷贝操作
Circle(const Circle&) = delete;
Circle& operator=(const Circle&) = delete;

private:
double _radius;
};

// 三角形类
class Triangle : public Figure {
public:
Triangle(double a, double b, double c)
: Figure(
[this]() { return "三角形"; },
[this]() {
double p = (_a + _b + _c) / 2;
return std::sqrt(p * (p - _a) * (p - _b) * (p - _c));
}),
_a(a), _b(b), _c(c) {}

// 移动构造函数
Triangle(Triangle&& other) noexcept
: Figure(std::move(other)),
_a(std::exchange(other._a, 0)),
_b(std::exchange(other._b, 0)),
_c(std::exchange(other._c, 0)) {}

// 移动赋值运算符
Triangle& operator=(Triangle&& other) noexcept {
if (this != &other) {
Figure::operator=(std::move(other));
_a = std::exchange(other._a, 0);
_b = std::exchange(other._b, 0);
_c = std::exchange(other._c, 0);
}
return *this;
}

// 禁用拷贝操作
Triangle(const Triangle&) = delete;
Triangle& operator=(const Triangle&) = delete;

private:
double _a, _b, _c;
};

// 显示图形信息的函数
void display(const Figure& fig) {
std::cout << fig.getName() << "的面积:" << fig.getArea() << std::endl;
}

// 测试函数
void test() {
// 创建图形对象
Rectangle rect(3, 4);
Circle circle(5);
Triangle triangle(3, 4, 5);

std::cout << "初始对象:" << std::endl;
display(rect);
display(circle);
display(triangle);

// 演示移动语义
std::cout << "\n移动后:" << std::endl;
Rectangle rect2 = std::move(rect);
Circle circle2 = std::move(circle);
Triangle triangle2 = std::move(triangle);

display(rect2);
display(circle2);
display(triangle2);
}

int main() {
test();
return 0;
}

实现解析

1. 从虚函数到 std::function 的转变

传统设计中使用纯虚函数virtual string getName() const = 0virtual double getArea() const = 0定义接口,这里我们用std::function替代:

1
2
using GetNameFunc = std::function<std::string()>;
using GetAreaFunc = std::function<double()>;

这种方式的优势在于:

  • 无需继承即可实现多态行为
  • 可以动态改变行为(通过替换函数对象)
  • 更容易组合不同的行为

2. 移动语义的实现

每个类都实现了移动构造函数和移动赋值运算符,同时禁用了拷贝操作:

1
2
3
4
5
6
7
8
9
// 移动构造函数
Rectangle(Rectangle&& other) noexcept
: Figure(std::move(other)),
_length(std::exchange(other._length, 0)),
_width(std::exchange(other._width, 0)) {}

// 禁用拷贝
Rectangle(const Rectangle&) = delete;
Rectangle& operator=(const Rectangle&) = delete;

使用std::exchange确保源对象在移动后处于有效但未指定的状态,这是移动语义的最佳实践。

3. Lambda 表达式的应用

在派生类构造函数中,我们使用 lambda 表达式初始化基类的函数对象:

1
2
3
4
5
Circle(double radius)
: Figure(
[this]() { return "圆形"; },
[this]() { return M_PI * _radius * _radius; }),
_radius(radius) {}

Lambda 捕获this指针,能够访问类的私有成员,实现了与传统成员函数相同的功能。