能否从std命名空间中的类(即std :: wstring)继承?

7

类std::wstring缺少一些针对“普通”c字符串(和字面量)的操作。

我想在自己的自定义类中添加这些缺失的操作:

#include <string>

class CustomWString : public std::wstring {

        public:

            CustomWString(const char*);
            const char* c_str(void);
};

这段代码在Ubuntu Karmic下使用g++ v4.4.1编译正常。

但是我想知道是否有反对意见?

编辑:以下是一些例子,以澄清我所说的“缺少操作”:

std::wstring foo("hello"); // requires the L() macro or something like that
std::string bar("hello");
std::wstring goose(bar.c_str());

foo="hello";  // requires the L() macro
foo=bar.c_str();
foo=bar;

编辑:我希望这个内容能够“集中”处理。因为我有一个项目需要从微软Windows平台移植过来,其中涉及到成千上万个失败的操作。

好消息是:有一个中心位置定义了要使用的字符串类型,例如:

#ifdef WINDOWS_OS
    typedef std::wstring AppStringClass;
#else
    typedef std::string AppStringClass;
#endif

1
缺少哪些操作? - GManNickG
@GMan:我猜是关于char*的转换。 - James McNellis
2
如果只是这样,从整个字符串类派生似乎有些过度。两个函数有什么问题吗? - GManNickG
6
我强烈建议将转换例程放在类外面。写std::wstring str = encode("str")并不会增加太多工作量。另外,请删除你的代码片段中的 void,它在那里完全没有用处。 - avakar
8个回答

15

这是合法的,但很多人认为不可取。问题在于标准库模板类没有虚析构函数。因此,如果您编写了以下代码:

std::wstring * p = new CustomWString ;
...
delete p;

您的行为未定义。


10

编译器肯定允许你这样做,因为在C ++中没有办法使用公共构造函数密封类。

STL容器不是为继承而设计,所以通常不是个好主意。 C ++有太多奇怪的角落可以忽略这一点。您的派生类可能会与另一个STL实现或仅与构建环境的更新冲突。

然而,我认为这是一个强烈的警告信号,而不是完全不行。这是一个“你真的非常确定自己在做什么吗?你知道所有影响,并且对所有替代方案有好的论据吗?”

在您的情况下:提供附加构造函数是可以接受的(因为公共接口已记录,并且您可以复制该接口),只要您不引入新的数据成员或VMT,切片或非虚拟析构符不是问题。(请记住,C ++标准声称通过基类指针删除时未定义行为没有虚拟DTor)

但是,我不确定您如何在不提供字符串存储空间的情况下实现char const * c_str()。一旦您引入新的数据成员,您就会进入地雷领域。


1
你对于“如何在不提供本地存储的情况下实现c_str()”是正确的。 - anon
在C++中,没有办法使用公共构造函数封装一个类。但是从C++11开始,添加了“final”关键字用于类和虚方法。我猜想目前没有任何标准库实现将任何stdlib类设置为“final”,因为(A)标准可能没有规定这一点,或者(B)这会破坏已经继承自stdlib类的所有代码,这可能是不必要甚至危险的。 - underscore_d

3

传统的反对从STL对象继承的论点是它们没有虚析构函数。因此,以vector为例,如果我要从它继承:

  class CMyVect : public std::vector<int>
  {
      ...
     int* someMemory;
  public:
     CMyVect()
     {
        someMemory = new int();
     }

     ~CMyVect()
     {
        delete someMemory;
     }
  };

基类中的非虚析构函数意味着,如果使用者将指向CMyVect的指针转换为指向int向量的指针,然后删除该指针,您的析构函数将不会被调用,导致内存泄漏。也就是说:

 int main()
 {
     std::vector<int>* vecPtr = new CMyVect();
     delete vecPtr; // invokes vector constructor only, not yours
 }

这并不是说你不能从这些对象继承,但这可能会导致问题。你必须小心地使用你继承的对象,或者干脆不需要让你的析构函数触发(不持有任何需要释放的资源)。


1
只有在引入新的数据成员时,缺少虚析构函数才成为一个问题。(这并不被标准保证,但编译器实现必须费力去打破它) - peterchen
1
@peterchen:假设派生类有一个或多个虚成员,而基类没有。那么我很确定通过基类删除会出错,因为(在典型实现中)它不指向内存分配的开头,但该块将被释放,就好像它确实指向了。编译器并没有特别处理这种情况。我认为最好建议避免未定义行为,而不是试图弄清楚在什么条件下“合理”的编译器实际上会做出可预测的事情。 - Steve Jessop
(另外,提问者的示例确实有一个新的数据成员)。 - Steve Jessop
3
@peterchen 标准明确表示,如果使用基类指针删除派生类对象时没有虚析构函数,将会导致行为未定义。事实上代码看起来正常运行并不代表行为是被定义好的。 - anon
@Steve:在这种情况下,我会考虑引入一个VMT作为新的数据成员-因为它会改变内存表示。... @Neil:这并不意味着每个类都需要虚拟DTor。虽然我同意我们已经超出了编译器所支持的范围。 - peterchen

3
我想你知道basic_string没有定义虚析构函数。在多态环境中使用wstring是错误的。否则,您可以像往常一样使用自定义类。
因此,以下做法是危险的:
vector<wstring*> mystrings;
mystrings.push_back(new CustomWString);
...
delete mystrings[0]; // No virtual destructor

直接使用您的自定义类是可以的,例如:

vector<CustomWString> mystrings; // OK
mystrings.push_back("...");

3
可以这样做,而且是合法的。但这是一个不好的主意。这些类不是为了继承而设计的,最好不要使用继承来扩展它们。如果你在使用这些类时不小心,可能会导致未定义的行为。(这些类没有虚析构函数)
此外,似乎添加这些特定的函数是个坏主意,因为它们依赖于区域设置和编码方式。你给这个类增加了超过一个职责。在charwchar_t之间转换的功能应该放在其他地方。

2

类模板std::basic_string(其中std::wstring是具有特定模板参数的typedef)没有虚析构函数,因此公开继承它是不好的想法


1

你可以这样做。但我认为没有意义,因为std::basic_string(以及其具体版本std::string和std::wstring)没有定义虚方法并且有非虚析构函数。因此,你无法在例如std::wstring的位置上正确使用你的子类。


是的,他们可以将其传递为“wstring”的替代品,但期望“wstring”的调用者只能调用基本行为,而不能调用OP添加的任何内容。这引发了一个问题,即为什么使用如此强(最强)的耦合性,如继承 - 如果没有同样强的理由,即Liskov可替换性。继承通常是一种懒惰的方式,可以从基础中获取所有方法,而无需按照组合所需的方式进行转发/包装。但是为什么不(A)做这项工作,(B)使用“public”组合,(C)使用自由函数?继承有很多潜在的缺陷,因此必须具有很大的好处。 - underscore_d

0
“继承”一个类的能力是一种语言特性。它只是一种正式、简单和干燥的语言特性,不与任何特定应用程序或任何特定使用模型相关联。它是一个低级工具,可以用于不同的目的。回答你的问题,从 std::wstring 类继承是完全合法的。
然而,下一个问题将是“为什么”。为什么要从 std::wstring 继承?
如果你在面向对象编程(OOP)范例中设计代码,那么公开地从 std::wstring 继承可能不是一个好主意,“可能”是这里的关键词。在 OOP 中,公共继承通常意味着多态类层次结构中的 IS-A 关系。类 std::wstring 并不是设计成多态的,这就是为什么公开从它继承可能被视为一件相当奇怪的事情。
如果你在不同的范例中设计代码,比如模板元编程(TMP),那么公共继承可能是完全有效的。你看,在 TMP 中,公共继承有完全不同的目的,与多态和关系没有任何关系。
换句话说,如果不考虑更大的背景,就没有办法对你的问题给出具体的答案。无论如何,要注意“机械”的回答,比如“由于std::wstring没有虚析构函数,所以你不能从中继承,因为这会破坏<proceed to illustrate with code>”。这样的推理是完全错误的,即使在面向对象编程的方法中也是如此。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接