使用引用作为依赖的类成员 [英] Using reference as class members for dependencies

查看:100
本文介绍了使用引用作为依赖的类成员的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我花费一些时间在内存管理的语言后,我会回到C ++,我很遗憾地失去了什么是最好的方式来实现依赖注入。 (我完全卖给DI,因为我发现它是使测试驱动设计很容易的最简单的方法)。



现在,浏览SO和google让我相当关于这件事的一些意见,我有点困惑。



作为此问题的答案, http: //stackoverflow.com/questions/352885/dependency-injection-in-c ,有人建议你不应该传递原始指针,即使是依赖注入。我明白它是与对象的所有权有关。



现在,对象的所有权也被处理了(虽然没有到足够的细节到我的状态;))臭名昭着的google风格指南:http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Smart_Pointers



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

  class Addict {
//我依赖的东西(因此,Addict name。sorry。)
依赖性* dependency_;
public:
Addict(Dependency * dependency):dependency_(dependency){
}
〜Addict(){
//不释放dependency_注入和你不拥有它!
}
void some_method(){
dependency _-> do_something();
}
// ... whatever ...
};

如果Dependency是一个纯虚类(aka poor-men-Interface)容易注入依赖的模拟版本(使用像google模拟的东西)。



问题是,我真的看不到我可以得到这样的代码,为什么我应该使用除了原始指针之外的其他任何东西!是不是不清楚依赖来自哪里?



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

  class Addict {
//我依赖的东西(因此,Addict的名字,抱歉)
const Dependency& dependency_;
public:
Addict(const依赖和依赖):dependency_(依赖){
}
〜Addict(){
//不释放dependency_它被注入,你不拥有它!
}
void some_method(){
dependency_.do_something();
}
// ... whatever ...
};

但我得到其他,同样权威的建议 http://billharlan.com/pub/papers/Managing_Cpp_Objects.html



正如你可以看到,我不知道各种方法的相对优缺点,所以我有点困惑。对不起,如果这已经讨论死亡,或者如果只是个人的选择和一个给定项目的一致性问题...但任何想法是欢迎的。



致谢



PH






/ strong>



(我不知道这是否是好的SO-tiquette这样做,但我会有代码示例, 。)



从各种响应,这里我可能最终会在我的情况下:




  • 传递依赖项作为参考(至少确保不允许NULL)

  • 在通常情况下不可能复制,明确禁止它,

  • 在可能进行复制的罕见情况下,将依赖关系存储为RAW指针

  • 让依赖项的创建者决定动态分配的堆栈分配(在这种情况下,通过智能指针进行管理)

  • 建立一个约束以从自己的资源中分离依赖关系



所以我最终会得到类似的结果:

  class NonCopyableAddict {
依赖& dep_dependency_;

//防止复制
NonCopyableAddict&运算符=(const NonCopyableAddict& other){}
NonCopyableAddict(const NonCopyableAddict& other){}

public:
NonCopyableAddict(依赖和依赖):dep_dependency_ ){
}
〜NonCopyableAddict(){
//没有风险尝试并删除对dep_dependency_的引用;
}
// ...
void so_some_stuff(){
dep_dependency_.some_function();
}
};

对于可复制类别:

  class CopyableAddict {
依赖性* dep_dependency_;

public:
//防止复制
CopyableAddict& operator =(const CopyableAddict& other){
//做任何有意义的...或让默认运算符工作?
}
CopyableAddict(const CopyableAddict& other){
//做任何有意义的...
}


CopyableAddict依赖和依赖):dep_dependency _(& dep_dependency){
}
〜CopyableAddict(){
//您可能会试图删除指针,但其名称以dep_,
//按照惯例你知道这不是你的工作
}
// ...
void so_some_stuff(){
dep_dependency _-> some_function();
}
};

从我的理解,没有办法表达我有一个指针东西,但我不拥有它,编译器可以强制执行。因此,我必须在这里使用命名约定...



正如Martin所指出的,以下示例不能解决问题。



复制构造函数,如:

  class Addict {
Dependency dependency_;
public:
Addict(const依赖和依赖):dependency_(依赖){
}
〜Addict(){
//不释放dependency_它被注入,你不拥有它!
}
void some_method(){
dependency_.do_something();
}
// ... whatever ...
};


解决方案

没有硬性规则:

由于人们已经提到使用引用内部对象可以导致复制问题(和它),所以它不是万能的,但对于某些情况下它可以是有用的(这是为什么C + +给我们的选项做这一切不同的方式)。但是使用RAW指针真的不是一个选项。如果你是动态分配对象,那么你应该总是使用智能指针来维护它们,你的对象也应该使用智能指针。



对于需要示例的人:Streams总是



您对代码示例的一些注释:



< h3>示例一和两个


您的第一个示例是指针。基本上与使用引用的第二个示例相同。区别是引用不能为NULL。当传递引用时,对象已经存活,因此应该具有比您已经测试的对象更长的生命周期(如果它是在堆栈上创建的),所以它应该是安全的保存引用。如果你是动态创建指针作为依赖,我会考虑使用boost :: shared_pointer或std :: auto_ptr,如果依赖的所有权是否共享。




示例三:




我没有看到第三个例子的任何好用。这是因为你不能使用多态类型(如果你传递一个派生自依赖的对象,它将在复制操作期间切片)。因此,代码也可以在Addict中,而不是单独的类。




Bill Harlen:(http://billharlan.com/ pub / papers /管理%5FCpp%5FObjects.html)



不要从Bill那里带走任何东西:


  1. 我从来没有听说过他。

  2. 他是一个地理物理学家而不是计算机程序员。

  3. 在Java中改进你的C ++

    • 现在,语言在用法上是完全错误的。


  4. 如果要使用待办事项/不要做的引用。

    然后,我将在C ++字段中选择一个Big名称:

    Stroustrup / Sutter / Alexandrescu / Meyers



摘要:




  1. 不要使用RAW指针(需要拥有权时)

  2. 请使用智能指针。









$ b b

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



 类Lexer 
{
public:Lexer (std :: istream&输入,std :: ostream&错误);
... 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派生自Lexer
CParser parser(lexer); // CParser derived from Parser

parser.parse();
}

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

parser.parse();
}


I am going back to C++ after spending some time in memory-managed languages, and I'm suddently kinda lost as to what is the best way to implement dependency injection. (I am completely sold to DI because I found it to be the simplest way to make test-driven design very easy).

Now, browsing SO and google got me quite a number of opinions on the matter, and I'm a bit confused.

As an answer to this question, http://stackoverflow.com/questions/352885/dependency-injection-in-c , someone suggested that you should not pass raw pointers around, even for dependency injection. I understand it is related to ownership of the objects.

Now, ownership of objects is also tackled (although not into enough details to my state ;) ) in the infamous google style guide : http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Smart_Pointers

So what I understand is that in order to make it clearer which object has ownership of which other objects, you should avoid passing raw pointers around. In particular, it seems to be against this kind of coding :

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 ... 
};    

If Dependency is a pure virtual class (aka poor-men-Interface ), then this code makes it easy to inject a mock version of the Dependency (using something like google mock).

The problem is, I don't really see the troubles I can get in with this kind of code, and why I should want to use anything else than raw pointers ! Is it that it is not clear where the dependency comes from ?

Also, I read quite a few posts hinting that one should really be using references in this situation, so is this kind of code better ?

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 ... 
};

But then I get other, equally authoritive advices against using references as member : http://billharlan.com/pub/papers/Managing_Cpp_Objects.html

As you can see I am not exactly sure about the relative pros and cons of the various approaches, so I am bit confused. I am sorry if this has been discussed to death, or if it is only a matter of personnal choice and consistency inside a given project ... but any idea is welcome.

Thanks

PH


Answer's summary

(I don't know if it is good SO-tiquette to do this, but I'll had code example for what I gathered from answers...)

From the various responses, here what I'll probably end up doing in my case :

  • pass dependencies as reference (at least to make sure NULL is not possible)
  • in the general case where copying is not possible, explicitely disallow it, and store dependencies as reference
  • in the rarer case where copying is possible, store dependencies as RAW pointers
  • let the creator of the dependencies (factory of some kind) decide between stack allocation of dynamic allocation (and in this case, management through a smart pointer)
  • establish a convention to separate dependencies from own resources

So I would end up with something like :

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 so_some_stuff() {
      dep_dependency_.some_function();
    }
};

And for a copyable class :

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 you job
    }
    //...
    void so_some_stuff() {
      dep_dependency_->some_function();
    }
};

From what I understood, there is no way to express the intent of "I have a pointer to some stuff, but I don't own it" that the compiler can enforce. So I'll have to resort to naming convention here ...


Kept for reference

As pointed by Martin, the following example does not solve the problem.

Or, assuming I have a copy constructor, something like :

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 ... 
};

解决方案

There is no hard and fast rule:
As people have mentioned using references inside objects can cause copy problems (and it does) so it is not a panacea, but for certain situation it can be useful (that is why C++ gives us the option to do it all these different ways). But using RAW pointers is really not an option. If you are dynamically allocating objects then you should always be maintaining them with smart pointers and your object should also be using smart pointers.

For people who demand examples: Streams are always passed and stored as references (as they can't be copied).

Some Comments on your code examples:

Example one and two

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.

Example Three:

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.

Bill Harlen: (http://billharlan.com/pub/papers/Managing%5FCpp%5FObjects.html)

Not to take anything away from Bill But:

  1. I have never heard of him.
  2. He is a Geo-Physists not a computer programmer
  3. He recomends programming in Java to improve your C++
    • The languages are now so different in usage that is utterly false).
  4. If you want to use references of What to-do/not to-do.
    Then I would pick one of the Big names in the C++ field:
    Stroustrup/Sutter/Alexandrescu/Meyers

Summary:

  1. Don't use RAW pointers (when ownership is required)
  2. Do use smart pointers.
  3. Don't copy objects into your object (it will slice).
  4. You can use references (but know the limitations).

My example of Dependency injection using references:

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();
}

这篇关于使用引用作为依赖的类成员的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆