欢迎来到 C++ 地狱。
您刚刚触及了 C++ 最具争议的方面之一:std::string 不是多态的,并且构造函数不是继承的。
唯一“干净”的方式(不会做出任何批评)是将 std::string 作为成员嵌入,委托其所有方法。干得好!
其他方式可以出现,但您必须始终注意一些限制。
- std::string 没有虚方法,所以如果你派生它,你将不会得到多态类型。
- 这意味着如果您将一个 Word 传递给一个字符串保持函数,并且该函数调用一个字符串方法,则不会调用您的覆盖方法并且
new
不得将Word via 的任何分配分配给字符串*:通过此类指针删除将导致未定义的行为
- 所有接受字符串并返回字符串-s 的继承方法都可以工作,但它们将返回字符串,而不是 Word。
关于构造函数,它们不是继承的。默认构造继承是一种错觉,因为编译器合成了默认、复制和分配的默认实现,隐式调用了基。
在 C++11 中,一种解决方法可以是
class Word: public std::string
{
public:
template<class... Args>
Word(Args&&... args) :std::string(std::forward<Args>(args)...)
{}
//whatever else
};
这使得在调用合适的 std::sting ctor 时要给出任何类型的参数(如果存在,否则会发生编译错误)。
现在,决定自己的设计应该是什么。可能您会使用普通的 std::string 和一组独立的免费函数。
另一种(不完美的)方法可以使 Word 不继承,而是嵌入 std::string,如上构造,并隐式转换为 std::string。(加上有一个 str() 显式方法)。这使您能够将 Word 用作字符串,从字符串创建 Word,但不能使用 Word“代替”字符串。
另一件事要忘记(可能来自Java ...):不要将自己固定为“is-a =继承和has-a =嵌入”OOP规则。所有 C++ 标准库对象都不是 OOP 意义上的对象,因此所有 OOP 学派方法论在这种情况下都有谬误。
您必须决定在您的情况下,简单编码(和“不要重复自己”范式的良好应用,继承更容易)或简单维护(嵌入让您的代码不太容易被使用)之间的权衡是什么被别人误会)
这是对以下评论的回答:
“标准 C++ 类缺乏多态性。为什么会这样?对于像我这样的新手来说,不使用虚函数实现标准 C++ 库似乎违背了它们旨在丰富语言的意义!!!"
嗯......是的,不是的!
由于您引用 PERL,请考虑 - PERL 是一种脚本语言,其中类型是动态的。- Java 是一种类型为静态而对象为动态的语言 - C++ 是一种类型为静态而对象为静态的语言(对象的动态分配是显式的)
现在,在 Java 中,对象总是动态分配的,局部变量是对这些对象的“引用”。在 C++ 中,局部变量本身就是对象,并且具有值语义。而C++标准库的设计不是作为一组可扩展的基础,而是作为一组通过模板生成代码的值类型。
把 an 想象成和作品std::string
一样int
工作的东西:你是否期望从 int 派生来获得“更多方法”或改变其中一些方法的行为?
这里有争议的方面是 - 为了与这个设计保持一致 - std::string 应该只管理它的内部存储器,但不应该有任何方法。相反,字符串函数应该被实现为模板,因此它们可以用作“算法”,以及任何其他表现出相同 std::string 外部行为的类。设计师没有的东西。
他们在上面放置了许多方法,但他们并没有使其保持多态性以仍然保留值语义,从而使设计变得模棱两可,并且保留继承是“重用”这些方法而不重新声明它们的唯一方法。这是可能的,但有我告诉你的限制。
如果您想有效地创建新功能,具有“价值多态性”,请使用 teplates:而不是
std::string capitalize(const std::string& s) { .... }
做类似的事情
template<class String>
String capitalize(const String& s) { .... }
这样您的代码就可以使用任何具有相同字符串接口的类,对于任何类型的字符。