关于继承:在C++中扩展库

Extending libraries in C++

是否可以在没有源代码的情况下从C++库扩展类?拥有头部是否足以允许您使用继承?我正在学习C++,我正在研究这个理论。我会测试这个,但我不知道怎么做。


简短回答是的,你当然可以。

长回答:警告:以下文字可能会伤害儿童一个敏感的OOP积分。如果你觉得或保持这样的感觉,那么远离这个答案:我的,你的和所有人的生活都会更容易。

让我揭开一个秘密:STL代码只不过是带有标题和库的规则C++代码,就像您的代码可以——而且最有可能做到。STL作者和你一样都是程序员。它们在编译器方面一点也不特殊。他们对此没有任何超能力。他们像你一样坐在马桶上,做你该做的。别把他们搞糊涂了。

STL代码遵循您自己编写的代码的完全相同的规则:将重写什么而不是基:总是如果它是虚拟的,并且只根据它的引用指针的静态类型,如果它不是虚拟的,像每块C++代码一样。不多不少。

重要的是不要破坏有关STL名称约定和语义的设计问题,这样代码的每一次进一步使用都不会混淆人们的期望,包括您自己,10年后阅读代码,不再记住某些决策。

例如,重写std::exception::what()必须返回一个解释性的持久C字符串(如stl文档所说),并且不能添加意外的其他模糊操作。

此外,覆盖流或流操作器应该按照整个设计的顺序来完成(您真的需要覆盖流还是只覆盖流缓冲区,或者只添加一个特定方面到它所嵌入的区域设置?):换句话说,不仅要学习"课程",还要学习它所有"世界"的设计,以正确理解它如何与周围的事物一起工作。

最后,但并非最不重要的一个最具争议的方面是容器和所有没有虚拟析构函数的东西。

我的观点是,关于"经典OOP规则:不要‘推导没有虚拟析构函数的东西’的噪音被夸大了:只是不要指望一个cow仅仅因为你在上面放置了一个saddle就变成一个horse

如果您需要(真的需要)一个类,该类使用与std::string完全相同的接口来管理一个字符序列,该接口能够隐式转换为std::string,并且具有更多内容,那么您有两种方法:

  • 做好女孩做的事,嵌入std:string并重写其所有112个(是的:它们超过100个)方法,其功能仅限于调用它们,并确保您仍然处女地与另一个好男孩程序员的代码结婚,或…
  • 当你发现这需要30年的时间,并且你冒着成为40岁的Y.O.处女的风险,没有一个好的男孩程序员会再感兴趣,更实际点,牺牲你的处女,然后得到std::string。你唯一要做的就是你有可能嫁给一个积分学家。你甚至可以发现这不一定是个问题:你甚至远离被他杀死的危险!

你唯一需要注意的是,由于std::string不是多态的,你的派生不会使它成为多态的,所以不要期望和std::string*std::string&引用yourstring调用你的方法,包括析构函数,这不是所有其他方法都特别尊重;它遵循完全相同的规则。但是…嘿,如果你嵌入并编写一个隐式转换操作符,你将得到确切的结果,不多不少!

规则很简单:不要让你自己的析构函数是虚拟的,也不要假装"OOP替换原则"与不是为OOP设计的东西一起工作,一切都会好起来的。

随着所有OOP积分者的需求在他们永恒的睡眠中不断增加,您的代码将工作,而他们仍然在重写100+std::string方法来嵌入它。


是的,类的声明足以从中派生。

当您链接到库时,其余代码将被提取。


是的,您可以在标准C++库中扩展类。头文件就足够了。

一些例子:

  • 扩展std::exception类以创建自定义异常
  • 扩展流库以在应用程序中创建自定义流

但是有一件事你应该注意,不要扩展没有virtual destructor的类。例如std::vectorstd::string

编辑:我刚才发现了这个问题的另一个问题,通过继承扩展C++标准库?


仅仅拥有一个头文件就足以继承该类。C++程序分为两个阶段:

  • 汇编编译器查找类型的定义并检查程序的语言正确性。这将生成对象文件。
  • 链接编译后的对象文件链接在一起形成一个可执行文件。

因此,只要有头文件(编译所需)和库(链接所需),就可以从类派生。但请注意,必须小心该类是否确实是用于继承的。例如:如果您有一个具有非virtual析构函数的类,那么该类就不是用于继承的。就像所有标准库容器类一样。

所以简而言之,仅仅拥有一个类的接口就足以进行派生,但是类的实现和设计语义确实起着重要的作用。