std::string
(以及所有标准类)都没有任何虚方法,因此创建一个带有虚方法的继承类将导致 UB(最可能是由于析构函数)。 (如果我错了,请纠正我)。
我曾认为在没有多态性的情况下进行继承是可以的,直到我在网上阅读了这个主题。
例如,在这个答案中:Why should one not derive from c++ std string class?给出了一些反对这种做法的论点。主要原因似乎是“切片问题”,当派生对象在代替std::string
参数传递给函数时,这将禁止添加功能,从而使非多态性不合理。惯用的C ++方式是创建自由函数,如果想要扩展string
的功能。我同意所有这些观点,尤其是因为我支持自由函数而不是单体类。
话虽如此,我认为我找到了一种情况,我认为实际上有必要从std::string
进行非多态继承。首先,我将展示我正在尝试解决的问题,然后我将展示为什么我认为从std::string
继承是最佳解决方案。
将一个用于调试目的的函数从C移植到C ++时,我意识到没有办法在C ++中创建格式化字符串(不使用类似于C的字符串函数,例如sprintf
)。也就是说:
C版本:void someKindOfError(const char *format, ...);
这将被调用:
someKindOfError("invalid argument %d, size = %d", i, size);
C++版本:void someKindOfError(const std::string &message);
调用此函数类似于:
std::stringstream ss;
ss << "invalid argument " << i << ", size = " << size;
someKindOfError(ss.str());
这个不可能只是一行代码,因为 <<
操作符返回一个 ostream
。所以需要额外两行代码和一个变量。
我想到的解决方案是创建一个名为 StreamString
的类,它继承自 std::string
(实际上是继承自 templated 的 BasicStreamString
,它又继承自 basic_string<>
,但这并不重要),在新功能方面,它具有类似于 stringstream
操作符的<<
操作符,以及转换成和转换自 string
的功能。
因此,前面的示例可以变为:
someKindOfError(StreamString() << "invalid argument " << i << ", size = " << size);
请记住,参数类型仍为const std::string&
该类已创建并完全可用。当需要即席创建字符串而不必声明额外的stringstream
变量时,我发现这个类在很多地方非常有用。这个对象可以像stringstream
一样进一步操作,但它实际上是一个string
,可以传递给期望string
的函数。
我为什么认为这是C++惯用法的例外:
- 当传递给期望
string
的函数时,该对象需要完全像一个string
一样运作,因此切片问题不是一个问题。 - 唯一(值得注意的)添加的功能是
operator<<
,我不愿意为标准string
对象重载它作为自由函数(这将在库中完成)。
我能想到的一个替代方法是创建一个可变参数模板自由函数。类似于:
template <class... Args>
std::string createString(Args... args);
这允许我们像这样进行调用:
someKindOfError(createString("invalid argument ", i , ", size = " , size));
这种替代方法的一个缺点是失去了像
stringstream
那样在创建后轻松操作字符串的能力。但我想我也可以创建一个自由函数来处理它。此外,人们通常使用运算符<<
来执行格式化插入。
总之:
- 我的解决方案是不是不好的实践(或最差的)?还是它是C++习语的例外,是可以接受的?
- 如果不好,有哪些可行的替代方案?
createString
可以吗?它能被改进吗?
StreamString
不需要从std::string
派生才能以这种方式使用。只需为其提供到std::string
或char const*
的转换运算符即可。 - Andy Prowlfinal
(上下文)关键字。 - Andy Prowl