为什么C++不允许向类添加新方法?

Why doesn't C++ allow adding new methods to classes?

本文关键字:添加 新方法 C++ 不允许 为什么      更新时间:2023-10-16

这似乎是一个相当任意的限制。正则方法不是就像C函数一样,有一个指向实例的参数吗?

如果是这样的话,我不明白为什么添加新方法会迫使我重新编译类的其余部分。为什么不允许通过单独的修正标题和单独的修正实现来添加方法呢。

考虑这个例子

  // in some header
  struct X
  {
      float func(float);
  };
  // and in another source file
  void caller()
  {
       X x;
       std::cout << x.func(2);     // will call X:func(float)
  }

现在假设我们决定添加一个接受intfunc()的新版本。

        // in some header
  struct X
  {
      float func(float);
      void func(int);
  };
  // and in another source file
  void caller()
  {
       X x;
       std::cout << x.func(2);
  }

如果caller()函数没有重新编译,就无法注册它正在调用的函数已经更改——它将在构建中继续调用X::func(float)

然后,可能是几个月后(在大型系统中,可能是几年后),另一个开发人员对caller()同一源文件中的一个函数进行了完全无关的更改。因此,源文件被重新生成。。。最后突然,那个人发现caller()不会编译——错误消息与他或她正在实现的代码的更改无关。

所有这些都发生在罪犯——引入新成员函数但没有触发重新编译和重建的程序员——不见踪影的时候。

留下来的开发人员要解决这个烂摊子。由于没有关于问题实际原因的信息,也没有关于为什么它昨天有效而今天无效的信息,没有关于如何正确解决问题的真正线索。。。。但仍然是要负责任的人。

这只是C++中的"任意限制"将防止的许多问题之一。

我想到了几件事。一方面,你需要声明方法的范围,我想这就是为什么你可以按照你建议的方式添加新运算符的原因。

另一方面,您在继承方面存在问题。编译器需要知道所有的虚拟方法才能将它们包含在vtable中。

正如deviationfan所说,这并不是真正的问题(假设你想添加一个常规(非虚拟)方法)。

$ for file in X.hh X.cc X-1.hh X-1.cc main.cc; do echo -e "n//--------------//$file"; cat "$file";  done                                                                                                                           
//--------------//X.hh
//X.hh
struct X {
   int foo(int);
};
//--------------//X.cc
//X.cc (available as X.o)
#include "X.hh"
int X::foo(int a){ return a+1; }
//--------------//X-1.hh
//X-1.hh
//copy X.hh and amend it
struct X {
   int foo(int);
   int bar(int);
};
//--------------//X-1.cc
//X-1.cc
#include "X-1.hh"
int X::bar(int a){ return a+2; }
//--------------//main.cc
//main.cc
#include "X-1.hh"
//^the latest definition
#include <iostream>
int main(){
  using namespace std;
  X x;
  cout << x.foo(1) << endl;
  cout << x.bar(1) << endl;

现在是构建部分:

$ make {X,X-1,main}.o
$ g++ {X,X-1,main}.o   #links correctly!
$ ./a.out
2
3

即使方法访问类/结构变量也有效。

TL;博士:

如果使用使用跟踪#include的依赖文件的构建系统,则可以make --assume-old一个仅通过简单的方法添加(没有重载或虚拟)而更改的头(或touch --date='10 minutes ago' changed_header.hh),因为依赖于类实例方法的旧子集的所有旧对象文件都不需要重新编译。

此外,正如AliciaBytes所指出的http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4474.pdf这基本上允许通过dot语法调用独立函数,所以这基本上相当于重新打开一个类来添加琐碎的函数。

重载函数并不是一个真正的问题,因为你总是#include一个类的特定表示形式(或者同一个类+一组特定的点语法可映射的独立函数),并且你可以有同一类的不同版本(相当于有一个类加上不同的点句法可映射的自立函数集)。(与虚拟函数不同,因为对象实例中有vtables和vtable指针)。