从C ++ STL容器派生有真正的风险吗? [英] Is there any real risk to deriving from the C++ STL containers?

查看:133
本文介绍了从C ++ STL容器派生有真正的风险吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

声称使用标准C ++容器作为基类是一个错误,这让我感到惊讶。



如果没有滥用语言来声明...

  // Example A 
typedef std :: vector< double>价格;
typedef std :: vector< double>收费;

...那么在声明中的危险是什么...

  //示例B 
class rates:public std :: vector< double> {
// ...
};
class费用:public std :: vector< double> {
// ...
};

对B的积极优势包括:




  • 启用函数重载,因为f(Rates&)和f(Charges&)是不同的签名

  • 价格>和X< Charges>

  • 调试程序可能会告诉您对象是费率还是费用

  • 如果,随着时间的推移,价格和收费会发展个性a Singleton for Rates,a output format for Charges—有一个明显的功能实现的范围。



A的积极优势包括:




    <
  • 十五岁的标准前编译器是唯一可以编译你的遗产的东西,不会窒息

  • 由于特殊化是不可能的,模板X< Rates>和模板X< Charges>



这两种方法都优于使用原始容器,因为如果实现从vector< double>到矢量< float>,只有一个地方可以更改B和也许只有一个地方用A更改(可能更多,因为有人可能在多个地方放置相同的typedef语句) / p>

我的目标是这是一个具体的,可回答的问题,而不是讨论更好或更差的做法。显示可能发生的最糟糕的事情,因为从标准容器中导出,这将通过使用typedef代替。



编辑:



毫无疑问,向类Rate或类Charges添加析构函数将是一种风险,因为std :: vector不会将其析构函数声明为virtual。在这个例子中没有析构函数,没有必要。销毁费率或费用对象将调用基类析构函数。这里也不需要多态性。挑战在于使用派生而不是typedef来显示不好的结果。



编辑:



请考虑此用例:

  #include< vector> 
#include< iostream>

void kill_it(std :: vector< double> * victim){
//用户代码,不知道费率或费用

//调用non- virtual_std :: vector< double>(),然后释放
//在地址victim处分配的内存
delete victim;

}

typedef std :: vector< double>价格;
class费用:public std :: vector< double> {};

int main(int,char **){
std :: vector< double> * p1,* p2;
p1 =新价格;
p2 =新费用;
// ???
kill_it(p2);
kill_it(p1);
return 0;
}

是否有任何可能的错误,即使一个随意的不幸的用户可以介绍? ??部分,这将导致与费用(派生类),但不是与率(typedef)的问题?



在Microsoft实现中,向量< T>本身是通过继承来实现的。向量< T,A>是从_Vector_Val< T,A>应该控制吗?

解决方案

标准容器没有虚拟析构函数,因此你不能处理它们。如果你不会,并且每个使用你的代码的人都不会,它本身不是错误。不过,为了清楚起见,你最好还是使用作曲。


The claim that it is a mistake ever to use a standard C++ container as a base class surprises me.

If it is no abuse of the language to declare ...

// Example A
typedef std::vector<double> Rates;
typedef std::vector<double> Charges;

... then what, exactly, is the hazard in declaring ...

// Example B
class Rates : public std::vector<double> { 
    // ...
} ;
class Charges: public std::vector<double> { 
    // ...
} ;

The positive advantages to B include:

  • Enables overloading of functions because f(Rates &) and f(Charges &) are distinct signatures
  • Enables other templates to be specialized, because X<Rates> and X<Charges> are distinct types
  • Forward declaration is trivial
  • Debugger probably tells you whether the object is a Rates or a Charges
  • If, as time goes by, Rates and Charges develop personalities — a Singleton for Rates, an output format for Charges — there is an obvious scope for that functionality to be implemented.

The positive advantages to A include:

  • Don't have to provide trivial implementations of constructors etc
  • The fifteen-year-old pre-standard compiler that's the only thing that will compile your legacy doesn't choke
  • Since specializations are impossible, template X<Rates> and template X<Charges> will use the same code, so no pointless bloat.

Both approaches are superior to using a raw container, because if the implementation changes from vector<double> to vector<float>, there's only one place to change with B and maybe only one place to change with A (it could be more, because someone may have put identical typedef statements in multiple places).

My aim is that this be a specific, answerable question, not a discussion of better or worse practice. Show the worst thing that can happen as a consequence of deriving from a standard container, that would have been prevented by using a typedef instead.

Edit:

Without question, adding a destructor to class Rates or class Charges would be a risk, because std::vector does not declare its destructor as virtual. There is no destructor in the example, and no need for one. Destroying a Rates or Charges object will invoke the base class destructor. There is no need for polymorphism here, either. The challenge is to show something bad happening as a consequence of using derivation instead of a typedef.

Edit:

Consider this use case:

#include <vector>
#include <iostream>

void kill_it(std::vector<double> *victim) { 
    // user code, knows nothing of Rates or Charges

    // invokes non-virtual ~std::vector<double>(), then frees the 
    // memory allocated at address victim
    delete victim ; 

}

typedef std::vector<double> Rates;
class Charges: public std::vector<double> { };

int main(int, char **) {
  std::vector<double> *p1, *p2;
  p1 = new Rates;
  p2 = new Charges;
  // ???  
  kill_it(p2);
  kill_it(p1);
  return 0;
}

Is there any possible error that even an arbitrarily hapless user could introduce in the ??? section which will result in a problem with Charges (the derived class), but not with Rates (the typedef)?

In the Microsoft implementation, vector<T> is itself implemented via inheritance. vector<T,A> is a publicly derived from _Vector_Val<T,A> Should containment be preferred?

解决方案

The standard containers do not have virtual destructors, thus you cannot handle them polymorphically. If you will not, and everyone who uses your code doesn't, it's not "wrong", per se. However, you are better off using composition anyway, for clarity.

这篇关于从C ++ STL容器派生有真正的风险吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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