【问题标题】:Constructor doesn't work for class inherited from std::string构造函数不适用于从 std::string 继承的类
【发布时间】:2009-02-17 05:28:43
【问题描述】:

什么时候做

#include <string>

class MyString : public std::string 
{
 public:    
   MyString() {}
};

但是下面的用法:

MyString s = "Happy day";
MyString s("Happy Day");
MyString s = (MyString)"Happy day";

它们都不起作用。

似乎与构造函数/运算符声明/覆盖有关,但谁能帮忙指出我在哪里可以找到这些资源?

谢谢。

【问题讨论】:

  • 当你继承时,你隐藏了基类的非默认构造函数。如果要调用这些构造函数,则必须在调用它们的子类中定义非默认构造函数。
  • 你不应该从 std::string 继承。您可以通过它没有虚拟析构函数的事实来判断这一点。你正在向许多潜在的问题敞开心扉。使 std::string 成为成员。

标签: c++ stl


【解决方案1】:

std::string 不是为继承而设计的。它没有任何虚函数(甚至没有析构函数!),所以你不能覆盖任何东西。它也没有受保护的接口,因此您无法从子类化中获得任何通过使用std::string 的独立实用程序函数无法获得的收益。

请记住,大多数 STL 实现都希望您使用带有复制语义的 std::string,而不是引用语义,这使得添加继承字段或覆盖函数的情况变得更弱。

如果您真的想要像 std::string 这样带有额外功能的东西,您可以考虑使用组合而不是继承,但这也不是很好。你不必担心std::string 析构函数没有被正确调用,但你最终不得不从std::string 包装很多你需要的方法,这很乏味。此外,您的实用程序函数仅在大多数代码都需要 std::string 时才能与 MyString 一起使用,因此它不是很可重用。

你最好制作一些使用std::string 的实用函数。或者,如果std::string 没有提供您需要的东西,您应该使用其他一些适合您需要的字符串实现。以下是我想到的一些可能性:

【讨论】:

  • 当然可以继承。你只需要确保它在编译时是可解析的。
  • 我没说你不能继承它。只是这样做你收获甚微。
  • @Charlie:你可以,它不会给你任何东西,因为你不能超载任何东西。你最好制作利用 std::string 方法的实用函数。
  • 从 std::string 继承是个坏主意。
  • 嗯。我没有意识到编辑 5 次会使事情成为社区 wiki。那好吧。随意抛光这个每个人!
【解决方案2】:

您需要为希望能够转换为字符串的不同类型定义一些构造函数。这些构造函数基本上可以将参数传递给底层的std::string

如果您不手动创建它们,编译器会为您创建一个默认构造函数和一个复制构造函数:

MyString() : std::string() { }
MyString(const MyString &other) : std::string(other) { }

要允许从字符串文字进行构造,您需要一个带有const char* 的构造函数:

MyString(const char* other) : std::string(other) { }

采用const std::string&amp; 的构造函数也可用于将std::strings 转换为您的字符串类型。如果你想避免普通字符串的隐式转换,你应该把它设为explicit

explicit MyString(const std::string &other) : std::string(other) { }

(已编辑,因为我的原始版本充满了错误,我无法删除已接受的答案)

【讨论】:

  • 感谢提供复制构造函数。
  • 我看不出 'MyString s("Happy Day")' 和 'MyString s("abc")' 之间的区别。你肯定错了。
  • 请记住,这允许编译器在解决重载等问题时将 MyString 替换为 std::string。这可能是您想要的,或者导致可怕的混乱。这两种情况我都见过。
【解决方案3】:

底线是您不应该这样做。 std::string 的析构函数不是虚拟的。这意味着,如果您执行以下操作:

std::vector<std::string*> s_vector;
s_vector.push_back(new MyString("Hello"));
s_vector.push_back(new std::string("World"));

const std::vector<std::string*>::iterator s_vector_end = s_vector.end();
std::vector<std::string*>::iterator s = s_vector.begin();
for (; s != s_vector_end; ++s)
{
    delete *s; // Error, MyString's destructor will
               // not be called, but std::string's!
}

这可能是安全的唯一方法是如果您不将成员添加到您的字符串。你可能认为你现在不需要任何东西,但是不知道这些问题的人可能会在以后出现(或者你,当你可能忘记了这个建议时)并添加一个,然后嘿,你有难以追踪的内存泄漏。

【讨论】:

    【解决方案4】:

    问题是需要重载接受const char*的构造函数,调用基类构造函数如下:

    class MyString : public std::string {
       public:    
          MyString() {}
          MyString( const char* c ) : std::string( c )  {}
    };
    

    那么你的所有三个测试都应该可以工作。

    【讨论】:

      【解决方案5】:

      std::string 不打算继承自。它没有任何虚拟方法,因此您不能覆盖它的任何方法。

      您应该研究构图。或者简单地创建在 std::strings 上运行的实用函数

      【讨论】:

        【解决方案6】:

        您正在定义一个不带参数的 ctor MyString。如果覆盖其他 ctor,则根本没有 ctor 采用字符串参数。

        您需要对const char * 类型的一个参数进行ctor,类似于

         MyString(const char * s): std::string(s){}
        

        (不要相信语法,查一下;我不再每天写C++了。)

        查看C++ FAQ Lite on ctors中的部分。

        (哎呀。Const char *,不是字符串。告诉你我不是每天都写C++。)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-09-29
          • 2012-09-03
          • 1970-01-01
          相关资源
          最近更新 更多