3

我正在学习 C++,想实现一个自定义字符串类,MyTextProc::Word添加一些功能std::string,例如字符串反转、大小写转换、翻译等。

似乎最好使用 is-a 关系来完成:

namespace MyTextProc {
   class Word : public string {
      /* my custom methods.... */
   };
}

我没有为我的类指定任何构造函数,但类的上述定义Word只公开了默认的 void 和复制构造函数 - 不能Word只继承所有公共字符串构造函数吗?

Wordstring. _ 我没有向字符串添加任何属性;为了实现我的新子类,我必须真正实现字符串的每一个构造函数,即基类吗?

使用has-a关系最好吗?我应该只实现一个const string&构造函数并要求客户端传递一个字符串对象进行构造吗?我应该覆盖所有的字符串构造函数吗?

4

2 回答 2

3

欢迎来到 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) { .... }

这样您的代码就可以使用任何具有相同字符串接口的类,对于任何类型的字符。

于 2013-02-24T18:34:11.027 回答
2

老实说,我会将您想要的方法实现为接收字符串并返回字符串的函数。它们将更容易测试、解耦和易于使用。在 C++ 中,不要总是在函数可以使用的情况下使用类。事实上,当您进入模板时,您可以创建一个没有定义的模板化函数,并为基本字符串类进行专门化。这样,您将始终知道您所接触的字符串类型是否具有自定义定义的方法(是的,如果您与 Microsoft 交互,您会发现有 5000 万个字符串实现。)

于 2013-02-24T18:14:06.817 回答