gpt4 book ai didi

c++11 - 没有原始指针的 C++11 中的依赖注入(inject)

转载 作者:行者123 更新时间:2023-12-04 02:02:08 26 4
gpt4 key购买 nike

我经常在我的项目中使用“依赖注入(inject)”模式。在 C++ 中,通过传递原始指针最容易实现,但现在使用 C++11,高级代码中的所有内容都应该可以使用智能指针来实现。但是这种情况下的最佳实践是什么?性能并不重要,干净易懂的代码现在对我来说更重要。

让我展示一个简化的例子。我们有一个内部使用距离计算的算法。我们希望能够用不同的距离度量(欧几里得、曼哈顿等)替换这个计算。我们的目标是能够这样说:

SomeAlgorithm algorithmWithEuclidean(new EuclideanDistanceCalculator());
SomeAlgorithm algorithmWithManhattan(new ManhattanDistanceCalculator());

但使用智能指针避免手动 newdelete .
这是使用原始指针的可能实现:
class DistanceCalculator {
public:
virtual double distance(Point p1, Point p2) = 0;
};

class EuclideanDistanceCalculator {
public:
virtual double distance(Point p1, Point p2) {
return sqrt(...);
}
};

class ManhattanDistanceCalculator {
public:
virtual double distance(Point p1, Point p2) {
return ...;
}
};

class SomeAlgorithm {
DistanceCalculator* distanceCalculator;

public:
SomeAlgorithm(DistanceCalculator* distanceCalculator_)
: distanceCalculator(distanceCalculator_) {}

double calculateComplicated() {
...
double dist = distanceCalculator->distance(p1, p2);
...
}

~SomeAlgorithm(){
delete distanceCalculator;
}
};

让我们假设复制不是一个真正的问题,如果我们不需要多态性,我们只需传递 DistanceCalculatorSomeAlgorithm 的构造函数按值(value)(复制)。但由于我们需要能够传入不同的派生实例(无需切片),因此参数必须是原始指针、引用或智能指针。

想到的一种解决方案是通过对 const 的引用传递它并将其封装在 std::unique_ptr<DistanceCalculator> 中。成员变量。那么电话将是:
SomeAlgorithm algorithmWithEuclidean(EuclideanDistance());

但是这个堆栈分配的临时对象(右值引用?)将在这一行之后被破坏。所以我们需要一些复制来使它更像一个值传递。但是由于我们不知道运行时类型,我们不能轻易地构造我们的副本。

我们还可以使用智能指针作为构造函数参数。由于所有权没有问题( DistanceCalculator 将归 SomeAlgorithm 所有)我们应该使用 std::unique_ptr .我真的应该用 unique_ptr 替换所有这些构造函数参数吗? ?它似乎降低了可读性。也是 SomeAlgorithm 的用户必须以一种尴尬的方式构造它:
SomeAlgorithm algorithmWithEuclidean(std::unique_ptr<DistanceCalculator>(new EuclideanDistance()));

或者我应该以某种方式使用新的移动语义(&&,std::move)?

这似乎是一个非常标准的问题,必须有一些简洁的方法来实现它。

最佳答案

如果我想这样做,我要做的第一件事就是杀死你的界面,而是使用这个:

SomeAlgorithm(std::function<double(Point,Point)> distanceCalculator_)

类型删除的调用对象。

我可以使用您的 EuclideanDistanceCalculator 进行替换像这样:
std::function<double(Point,Point)> UseEuclidean() {
auto obj = std::make_shared<EuclideanDistance>();
return [obj](Point a, Point b)->double {
return obj->distance( a, b );
};
}
SomeAlgorithm foo( UseEuclidean() );

但由于距离计算器很少需要状态,我们可以取消对象。

在 C++1y 支持下,这缩短为:
std::function<double(Point,Point>> UseEuclidean() {
return [obj = std::make_shared<EuclideanDistance>()](Point a, Point b)->double {
return obj->distance( a, b );
};
}

由于它不再需要局部变量,因此可以内联使用:
SomeAlgorithm foo( [obj = std::make_shared<EuclideanDistance>()](Point a, Point b)->double {
return obj->distance( a, b );
} );

但同样, EuclideanDistance没有任何真实的状态,所以我们可以
std::function<double(Point,Point>> EuclideanDistance() {
return [](Point a, Point b)->double {
return sqrt( (b.x-a.x)*(b.x-a.x) + (b.y-a.y)*(b.y*a.y) );
};
}

如果我们真的不需要运动但我们确实需要状态,我们可以写一个 unique_function< R(Args...) >不支持基于非移动的分配的类型,并存储其中之一。

这个的核心是接口(interface) DistanceCalculator是噪音。变量的名称通常就足够了。 std::function< double(Point,Point) > m_DistanceCalculator它的作用很清楚。类型删除对象的创建者 std::function处理任何生命周期管理问题,我们只存储 function按值对象。

如果您的实际依赖注入(inject)更复杂(例如多个不同的相关回调),那么使用接口(interface)也不错。如果您想避免复制要求,我会这样做:
struct InterfaceForDependencyStuff {
virtual void method1() = 0;
virtual void method2() = 0;
virtual int method3( double, char ) = 0;
virtual ~InterfaceForDependencyStuff() {}; // optional if you want to do more work later, but probably worth it
};

然后,写下你自己的 make_unique<T>(Args&&...) (一个 std 一个在 C++1y 中出现),并像这样使用它:

界面:
SomeAlgorithm(std::unique_ptr<InterfaceForDependencyStuff> pDependencyStuff)

采用:
SomeAlgorithm foo(std::make_unique<ImplementationForDependencyStuff>( blah blah blah ));

如果你不想要 virtual ~InterfaceForDependencyStuff()并想使用 unique_ptr ,您必须使用 unique_ptr存储它的删除器(通过传入一个有状态的删除器)。

另一方面,如果 std::shared_ptr已经带有 make_shared , 默认情况下,它有状态地存储其删除器。所以如果你选择 shared_ptr存储您的界面,您将获得:
SomeAlgorithm(std::shared_ptr<InterfaceForDependencyStuff> pDependencyStuff)


SomeAlgorithm foo(std::make_shared<ImplementationForDependencyStuff>( blah blah blah ));

make_shared将存储删除 ImplementationForDependencyStuff的函数指针转换为 std::shared_ptr<InterfaceForDependencyStuff> 时不会丢失,因此您可以放心地缺少 virtual InterfaceForDependencyStuff 中的析构函数.我个人不会打扰,离开 virtual ~InterfaceForDependencyStuff那里。

关于c++11 - 没有原始指针的 C++11 中的依赖注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23477589/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com