问题描述
我想问一下有效C ++中的一个具体问题。
I wanted to ask about a specific point made in Effective C++.
它说:
我不明白在一个类中需要什么具体来作为基类(而不是多态) / p>
是我不应该从 std :: string
派生的唯一原因是它没有虚拟析构函数?为了可重用性目的,可以定义一个基类,并且多个派生类可以继承它。那么什么使 std :: string
甚至不合格作为基类呢?
I do not understand what specifically is required in a class to be eligible for being a base class (not a polymorphic one)?
class纯粹为可重用性目的定义,并且有许多派生类型,是否有任何方法来阻止客户端做 Base * p = new Derived()
,因为类不是
Is the only reason that I should not derive from std::string
class is it does not have a virtual destructor? For reusability purpose a base class can be defined and multiple derived class can inherit from it. So what makes std::string
not even eligible as a base class?
推荐答案
我认为这种说法反映了这里的混乱(强调我):
I think this statement reflects the confusion here (emphasis mine):
在惯用的C ++中,有两种用于从类派生的用法:
In idiomatic C++, there are two uses for deriving from a class:
- private 继承,用于使用模板的mixins和面向方面的编程。
- em>多态性情况。 EDIT :好的,我想这可以在几个mixin场景中使用,例如
boost :: iterator_facade
- 显示正在使用中。
- private inheritance, used for mixins and aspect oriented programming using templates.
- public inheritance, used for polymorphic situations only. EDIT: Okay, I guess this could be used in a few mixin scenarios too -- such as
boost::iterator_facade
-- which show up when the CRTP is in use.
如果你不想做多态性,那么绝对没有理由在C ++中公开派生类。该语言具有免费功能作为语言的标准功能,而自由功能是您应该在这里使用。
There is absolutely no reason to publicly derive a class in C++ if you're not trying to do something polymorphic. The language comes with free functions as a standard feature of the language, and free functions are what you should be using here.
这样想 - 你真的想强制你的代码的客户端转换为使用一些专有的字符串类,只是因为你想解决一些方法?因为与Java或C#(或最类似的面向对象的语言)不同,当你在C ++中派生一个类时,基类的大多数用户需要知道这种变化。在Java / C#中,类通常通过引用访问,这类似于C ++的指针。因此,有一个间接级别,可以解耦你的类的客户端,允许你替换一个派生类没有其他客户端知道。
Think of it this way -- do you really want to force clients of your code to convert to using some proprietary string class simply because you want to tack on a few methods? Because unlike in Java or C# (or most similar object oriented languages), when you derive a class in C++ most users of the base class need to know about that kind of a change. In Java/C#, classes are usually accessed through references, which are similar to C++'s pointers. Therefore, there's a level of indirection involved which decouples the clients of your class, allowing you to substitute a derived class without other clients knowing.
但是,在C ++中,类值类型 - 与大多数其他OO语言不同。最简单的查看方式是所谓的。基本上,请考虑:
However, in C++, classes are value types -- unlike in most other OO languages. The easiest way to see this is what's known as the slicing problem. Basically, consider:
int StringToNumber(std::string copyMeByValue)
{
std::istringstream converter(copyMeByValue);
int result;
if (converter >> result)
{
return result;
}
throw std::logic_error("That is not a number.");
}
如果您将自己的字符串传递给此方法,<$将调用c $ c> std :: string 来创建副本,不是您的派生对象的复制构造函数 - 无论 std :: string
。这可能导致您的方法和附加到字符串的任何方法之间的不一致。函数 StringToNumber
不能简单地接受任何你的派生对象,并复制,只是因为你的派生对象可能有一个不同的大小比 std :: string
- 但是此函数已编译为仅在自动存储中保留 std :: string
的空间。在Java和C#这不是一个问题,因为像自动存储涉及的唯一的事情是引用类型,并且引用总是相同的大小。
If you pass your own string to this method, the copy constructor for std::string
will be called to make a copy, not the copy constructor for your derived object -- no matter what child class of std::string
is passed. This can lead to inconsistency between your methods and anything attached to the string. The function StringToNumber
cannot simply take whatever your derived object is and copy that, simply because your derived object probably has a different size than a std::string
-- but this function was compiled to reserve only the space for a std::string
in automatic storage. In Java and C# this is not a problem because the only thing like automatic storage involved are reference types, and the references are always the same size. Not so in C++.
长篇小说 - 不要使用继承来解决C ++中的方法。这不是惯用的,并导致语言的问题。在可能的情况下使用非朋友,非成员函数,然后组合。不要使用继承,除非你是模板元编程或想要多态行为。有关详细信息,请参阅Scott Meyers的项目23:将非成员非朋友函数优先于成员函数。
Long story short -- don't use inheritance to tack on methods in C++. That's not idiomatic and results in problems with the language. Use non-friend, non-member functions where possible, followed by composition. Don't use inheritance unless you're template metaprogramming or want polymorphic behavior. For more information, see Scott Meyers' Effective C++ Item 23: Prefer non-member non-friend functions to member functions.
编辑:这是一个更完整的例子显示切片问题。您可以在上看到它的输出。
Here's a more complete example showing the slicing problem. You can see it's output on codepad.org
#include <ostream>
#include <iomanip>
struct Base
{
int aMemberForASize;
Base() { std::cout << "Constructing a base." << std::endl; }
Base(const Base&) { std::cout << "Copying a base." << std::endl; }
~Base() { std::cout << "Destroying a base." << std::endl; }
};
struct Derived : public Base
{
int aMemberThatMakesMeBiggerThanBase;
Derived() { std::cout << "Constructing a derived." << std::endl; }
Derived(const Derived&) : Base() { std::cout << "Copying a derived." << std::endl; }
~Derived() { std::cout << "Destroying a derived." << std::endl; }
};
int SomeThirdPartyMethod(Base /* SomeBase */)
{
return 42;
}
int main()
{
Derived derivedObject;
{
//Scope to show the copy behavior of copying a derived.
Derived aCopy(derivedObject);
}
SomeThirdPartyMethod(derivedObject);
}
这篇关于为什么不应该从c ++ std字符串类派生?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!