gpt4 book ai didi

c++ - 使用引用作为依赖的类成员

转载 作者:IT老高 更新时间:2023-10-28 13:01:29 31 4
gpt4 key购买 nike

在使用内存管理语言一段时间后,我将回到 C++,但我突然有点不知道什么是实现依赖注入(inject)的最佳方法。 (我完全卖给了 DI,因为我发现它是使测试驱动设计变得非常简单的最简单方法。

现在,浏览 SO 和 google 让我对此事有很多意见,我有点困惑。

作为这个问题的答案,Dependency injection in C++ ,有人建议你不应该传递原始指针,即使是依赖注入(inject)。我知道这与对象的所有权有关。

现在,在臭名昭著的谷歌风格指南中也处理了对象的所有权(尽管对我的状态来说还不够详细;)):http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Smart_Pointers

所以我的理解是,为了更清楚哪个对象拥有其他对象的所有权,您应该避免传递原始指针。特别是,它似乎反对这种编码:

class Addict {
// Something I depend on (hence, the Addict name. sorry.)
Dependency * dependency_;
public:
Addict(Dependency * dependency) : dependency_(dependency) {
}
~Addict() {
// Do NOT release dependency_, since it was injected and you don't own it !
}
void some_method() {
dependency_->do_something();
}
// ... whatever ...
};

如果 Dependency 是一个纯虚拟类(又名穷人接口(interface)),那么这段代码可以很容易地注入(inject) Dependency 的模拟版本(使用类似 google mock 的东西)。

问题是,我真的不明白使用这种代码会遇到什么麻烦,以及为什么我应该使用原始指针以外的任何东西!是不是不清楚依赖从何而来?

另外,我读了很多帖子,暗示在这种情况下确实应该使用引用,那么这种代码更好吗?

class Addict {
// Something I depend on (hence, the Addict name. sorry.)
const Dependency & dependency_;
public:
Addict(const Dependency & dependency) : dependency_(dependency) {
}
~Addict() {
// Do NOT release dependency_, since it was injected and you don't own it !
}
void some_method() {
dependency_.do_something();
}
// ... whatever ...
};

然后我得到其他同样权威的建议反对使用引用作为成员:http://billharlan.com/pub/papers/Managing_Cpp_Objects.html

如您所见,我不确定各种方法的相对优缺点,所以我有点困惑。如果这个问题已经被讨论到死,或者只是个人选择和给定项目中的一致性问题,我很抱歉......但欢迎任何想法。


答案摘要

(我不知道这样做是否是好的礼仪,但我会为我从答案中收集到的内容添加代码示例......)

从各种回复中,我可能最终会这样做:

  • 将依赖项作为引用传递(至少要确保 NULL 是不可能的)
  • 在无法复制的一般情况下,明确禁止复制,并将依赖项存储为引用
  • 在可以复制的极少数情况下,将依赖项存储为 RAW 指针
  • 让依赖项的创建者(某种工厂)在堆栈分配或动态分配之间做出决定(在后一种情况下,通过智能指针进行管理)
  • 建立将依赖项与自有资源分开的约定

所以我最终会得到类似的东西:

class NonCopyableAddict {
Dependency & dep_dependency_;

// Prevent copying
NonCopyableAddict & operator = (const NonCopyableAddict & other) {}
NonCopyableAddict(const NonCopyableAddict & other) {}

public:
NonCopyableAddict(Dependency & dependency) : dep_dependency_(dep_dependency) {
}
~NonCopyableAddict() {
// No risk to try and delete the reference to dep_dependency_ ;)
}
//...
void do_some_stuff() {
dep_dependency_.some_function();
}
};

对于可复制的类:

class CopyableAddict {
Dependency * dep_dependency_;

public:
// Prevent copying
CopyableAddict & operator = (const CopyableAddict & other) {
// Do whatever makes sense ... or let the default operator work ?
}
CopyableAddict(const CopyableAddict & other) {
// Do whatever makes sense ...
}


CopyableAddict(Dependency & dependency) : dep_dependency_(&dep_dependency) {
}
~CopyableAddict() {
// You might be tempted to delete the pointer, but its name starts with dep_,
// so by convention you know it is not your job
}
//...
void do_some_stuff() {
dep_dependency_->some_function();
}
};

据我了解,没有办法表达编译器可以强制执行的“我有一个指向某些东西的指针,但我不拥有它”的意图。所以我不得不在这里求助于命名约定......


留作引用

正如马丁所指出的,下面的例子并没有解决问题。

或者,假设我有一个复制构造函数,例如:

class Addict {
Dependency dependency_;
public:
Addict(const Dependency & dependency) : dependency_(dependency) {
}
~Addict() {
// Do NOT release dependency_, since it was injected and you don't own it !
}
void some_method() {
dependency_.do_something();
}
// ... whatever ...
};

最佳答案

没有硬性规定:
正如人们所提到的,在对象内部使用引用可能会导致复制问题(确实如此),因此它不是万能药,但在某些情况下它可能很有用(这就是为什么 C++ 为我们提供了以所有这些不同方式执行此操作的选项)。但是使用 RAW 指针确实不是一种选择。如果您正在动态分配对象,那么您应该始终使用智能指针来维护它们,并且您的对象也应该使用智能指针。

对于需要示例的人:流始终作为引用传递和存储(因为它们不能被复制)。

对您的代码示例的一些评论:

示例一和二

Your first example with pointers. Is basically the same as the second example using references. The difference being that a reference can not be NULL. When you pass a reference the object is already alive and thus should have a lifespan greater than the object you are testing already (If it was created on the stack) so it should be safe to keep a reference. If you are dynamically creating pointers as dependencies I would consider using boost::shared_pointer or std::auto_ptr depending if ownership of the dependency is shared or not.

示例三:

I don't see any great use for your third example. This is because you can not use polymorphic types (If you pass an object derived from Dependency it will be sliced during the copy operation). Thus the code may as well be inside Addict rather than a separate class.

比尔·哈伦:( http://billharlan.com/pub/papers/Managing%5FCpp%5FObjects.html )

不要从 Bill But 身上拿走任何东西:

  1. 我从未听说过他。
    • 他是地球物理学家而不是计算机程序员
    • 他建议使用 Java 编程来改进您的 C++
    • 现在语言的用法如此不同,这完全是错误的)。
    • 如果你想使用做什么/不做什么的引用。
      然后我会选择 C++ 领域的大人物之一:
      Stroustrup/Sutter/Alexandrescu/迈耶斯

总结:

  1. 不要使用 RAW 指针(需要所有权时)
  2. 使用智能指针。
  3. 不要将对象复制到您的对象中(它会切片)。
  4. 您可以使用引用资料(但要知道限制)。

我使用引用的依赖注入(inject)示例:

class Lexer
{
public: Lexer(std::istream& input,std::ostream& errors);
... STUFF
private:
std::istream& m_input;
std::ostream& m_errors;
};
class Parser
{
public: Parser(Lexer& lexer);
..... STUFF
private:
Lexer& m_lexer;
};

int main()
{
CLexer lexer(std::cin,std::cout); // CLexer derived from Lexer
CParser parser(lexer); // CParser derived from Parser

parser.parse();
}

// In test.cpp
int main()
{
std::stringstream testData("XXXXXX");
std::stringstream output;
XLexer lexer(testData,output);
XParser parser(lexer);

parser.parse();
}

关于c++ - 使用引用作为依赖的类成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1974682/

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