为什么不应该从c++;标准字符串类? 我想问一下C++中的一个特定点。

为什么不应该从c++;标准字符串类? 我想问一下C++中的一个特定点。,c++,string,inheritance,stl,C++,String,Inheritance,Stl,它说: 如果类需要像多态类一样工作,则应该将析构函数设置为虚拟的。它进一步补充说,由于std::string没有虚拟析构函数,因此永远不应该从它派生。另外,std::string甚至不被设计为基类,忘记多态基类吧 我不明白一个类要成为一个基类(不是多态类)具体需要什么 我不应该从std::string类派生的唯一原因是它没有虚拟析构函数吗?出于可重用性的目的,可以定义一个基类,并且可以从中继承多个派生类。那么是什么使得std::string甚至不能作为基类呢 另外,如果有一个纯粹为了可重用性而定

它说:

如果类需要像多态类一样工作,则应该将析构函数设置为虚拟的。它进一步补充说,由于
std::string
没有虚拟析构函数,因此永远不应该从它派生。另外,
std::string
甚至不被设计为基类,忘记多态基类吧

我不明白一个类要成为一个基类(不是多态类)具体需要什么

我不应该从
std::string
类派生的唯一原因是它没有虚拟析构函数吗?出于可重用性的目的,可以定义一个基类,并且可以从中继承多个派生类。那么是什么使得
std::string
甚至不能作为基类呢


另外,如果有一个纯粹为了可重用性而定义的基类,并且有许多派生类型,那么有没有办法阻止客户机执行
base*p=new-derived()< /代码>因为类不是要被多态地使用的,

,C++标准声明如果基类析构函数不是虚的,并且删除一个基类对象,指向派生类的对象,则会导致一个未定义的行为。 C++标准第5.3.5/3节:

如果操作数的静态类型与其动态类型不同,则静态类型应为操作数动态类型的基类,且静态类型应具有虚拟析构函数或行为未定义

明确非多态类和虚拟析构函数的需求
使析构函数虚拟化的目的是通过delete表达式方便对象的多态删除。如果没有多态删除对象,那么就不需要虚拟析构函数

为什么不从字符串类派生?
通常应该避免从任何标准容器类派生,因为它们没有虚拟析构函数,这使得不可能以多态方式删除对象。
至于string类,string类没有任何虚拟函数,因此没有任何可以重写的内容。你所能做的就是隐藏一些东西


如果您想拥有类似字符串的功能,您应该编写自己的类,而不是从std::string继承。

不仅析构函数不是虚拟的,std::string根本不包含虚拟函数,也不包含受保护的成员。这使得派生类很难修改其功能

那你为什么要从中得到什么呢

非多态性的另一个问题是,如果您将派生类传递给一个需要字符串参数的函数,您额外的功能将被切掉,对象将再次被视为普通字符串。

如果您确实想从它派生(不讨论为什么要这样做)我认为您可以通过将
操作符设为新的
私有来防止
派生的
类直接堆实例化:

class StringDerived : public std::string {
//...
private:
  static void* operator new(size_t size);
  static void operator delete(void *ptr);
}; 

但是通过这种方式,您可以将自己限制在任何动态
StringDerived
对象之外。

我认为这种说法反映了这里的困惑(我的重点):

我不明白在一个类中有什么特别的要求才有资格成为基本类(不是多态类

在习惯C++中,派生类有两种用法:

  • 私有继承,用于混合和使用模板的面向方面编程
  • public继承,仅用于多态情况EDIT:好的,我想这也可以用于一些混合场景中——比如
    boost::iterator\u facade
    ——在使用时显示
如果你不想做一些多态的事情,那么绝对没有理由公开C++中的一个类。作为语言的标准功能,该语言附带了自由函数,您应该在这里使用自由函数

这样想吧——你真的想强迫你的代码的客户端转换成使用一些专有的字符串类,仅仅因为你想增加一些方法吗?因为不像java或C++语言(或者大多数类似的面向对象语言),当你在C++中派生类时,基类的大多数用户需要知道这种改变。在Java/C#中,类通常通过引用进行访问,这类似于C++的指针。因此,这里涉及到一个间接层,它将类的客户机解耦,允许您在其他客户机不知道的情况下替换派生类

但是,在C++中,类是强的>值类型< /强>——不像大多数其他OO语言。最简单的方法就是我们所知道的。基本上,考虑:

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.");
}
如果将自己的字符串传递给此方法,则将调用
std::string
的复制构造函数进行复制,而不是派生对象的复制构造函数——无论传递的是
std::string
的哪个子类。这可能导致您的方法与任何附加到字符串的内容之间不一致。函数
StringToNumber
不能简单地获取并复制您的派生对象,这仅仅是因为您的派生对象的大小可能与
std::string
不同,但此函数的编译目的是在自动存储中仅为
std::string
保留空间。在Java和C#中,这不是一个问题,因为像自动存储这样涉及的唯一东西是引用类型,并且引用的大小总是相同的。在C++中不是这样。

长篇短-不要用继承来粘贴C++中的方法。这不是惯用语,会导致问题

#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);
}
class Derived : protected Base { // 'protected' to avoid Base* p = new Derived
  const char* c_str () const { return Base::c_str(); }
//...
};