为什么我不能在C++中继承int?

Roc*_*net 58 c++ inheritance integer language-design strong-typing

我很想能够做到这一点:

class myInt : public int
{

};
Run Code Online (Sandbox Code Playgroud)

为什么我不能?

我为什么要这样?更强的打字.例如,我可以定义两个类intAintB,这让我做intA + intAintB + intB,但不是intA + intB.

"Ints不是课程." 所以呢?

"Ints没有任何会员数据." 是的,他们有,他们有32位,或其他什么.

"Ints没有任何会员功能." 好吧,他们有很多像+和的运营商-.

Jer*_*fin 82

尼尔的评论非常准确.Bjarne提到考虑并拒绝这种确切的可能性1:

初始化器语法过去对于内置类型是非法的.为了实现它,我介绍了内置类型具有构造函数和析构函数的概念.例如:

int a(1);    // pre-2.1 error, now initializes a to 1
Run Code Online (Sandbox Code Playgroud)

我考虑扩展这个概念,允许从内置类派生,并为内置类型显式声明内置运算符.但是,我克制了自己.

int与拥有int成员相比,允许从a派生实际上并没有给C++程序员带来任何新的东西.这主要是因为int没有任何虚拟函数可以覆盖派生类.更严重的是虽然,C转换规则是如此混乱是假装int, short等等,都是乖巧普通班是行不通的.它们要么是C兼容的,要么它们遵守相对良好的C++规则,但不能同时遵循两者.

就评论而言,表现不能成为一个类,它(至少大部分)都是假的.在Smalltalk中,所有类型都是类 - 但几乎所有Smalltalk实现都有优化,因此实现可以与您使非类类型工作的方式基本相同.例如,smallInteger类表示一个15位整数,并且'+'消息被硬编码到虚拟机中,因此即使您可以从smallInteger派生,它仍然提供类似于内置类型的性能(虽然Smalltalk与C++的不同之处在于,直接的性能比较很难并且不太可能意味着很多.

编辑:在C或C++中可能不需要SmallInteger的Smalltalk实现中"浪费"的那一位.Smalltalk有点像Java - 当你"定义一个对象"时,你实际上只是定义一个指向对象的指针,你必须动态分配一个对象来指向它.您操作的内容,作为参数传递给函数等,始终只是指针,而不是对象本身.

不是 littleInteger的实现方式 - 在这种情况下,它们将整数值直接放入通常是指针的位置.为了区分smallInteger和指针,它们强制所有对象在偶数字节边界处分配,因此LSB始终是清空的.smallInteger总是设置LSB.

然而,大多数情况是必要的,因为Smalltalk是动态类型的 - 它必须能够通过查看值本身来推断出类型,而smallInteger基本上使用该LSB作为类型标记.鉴于C++是静态类型的,从来没有必要从值中推断出类型,所以你可能不需要浪费那一点.

1C++的设计和演变中,§15.11.3.


3Da*_*ave 53

Int是序数类型,而不是类.你为什么想要?

如果需要向"int"添加功能,请考虑构建具有整数字段的聚合类,以及公开所需的任何其他功能的方法.

更新

@OP "Ints不是班级"所以?

继承,多态和封装是面向对象设计的基石.这些都不适用于序数类型.你不能从int继承,因为它只是一堆字节而且没有代码.

Int,chars和其他序数类型没有方法表,因此无法添加方法或覆盖它们,这实际上是继承的核心.

  • 为什么有人设计本田Aztek看起来如此愚蠢?这是一个设计决定.这是唯一的原因. (11认同)
  • 好的,@ Sobox,我会向你提出同样的问题,就像我对尼尔在对梅森回答的评论中所做的那样.如果继承没有虚函数的东西没有任何好处,那么为什么C++设计允许它呢?C++允许允许从没有虚函数的类类型中降序,因此int缺少虚函数不能成为C++禁止它下降的原因. (5认同)
  • 所有我想知道的是*为什么*.是否存在一些在C++的设计约束中无法完成的根本原因,还是他们忘记了? (3认同)
  • @snicker这是相当OT,但阿兹特克人是庞蒂亚克,而不是本田. (3认同)
  • @Rocketmagnet,我认为没有人在争论允许你扩展原始类型的语言的优点(许多语言都可以).但是,它在C++中是不可能的.在C++中,最好的解决方案是构建一个聚合类,该聚类具有此答案建议的整数成员. (2认同)
  • @Rocketmagnet - 它来自C遗产以及原始人所代表的东西.c ++中的原语只是一个字节集合,除了编译器之外几乎没有什么意义.另一方面,类有一个函数表,一旦你开始继承继承和虚拟继承路径,那么你就有了一个vtable.这些都不存在于一个原语中,并且通过使它存在你将a)打破许多假定int是8个字节的c代码和b)使程序占用更多的内存. (2认同)
  • Mason,声明int的子类型是合法的,不需要向int添加任何存储空间.它特别不需要向int添加虚函数.如果我有一个具有*添加*虚函数的int子类,并将其分配给普通的int变量,那么我有一个切片问题,但这并不是这种情况所特有的. (2认同)
  • @Rob如果我从int继承它最好有一个虚拟析构函数. (2认同)
  • 关于本田; 这是一个很糟糕的比喻.问:为什么本田Aztek有四个轮子?坏 - 答:因为汽车有4个轮子.好的答:三个轮子已经尝试过,而且不稳定. (2认同)

jal*_*alf 25

我为什么要这样?更强的打字.例如,我可以定义两个类intA和intB,这使我可以执行intA + intA或intB + intB,但不能执行intA + intB.

这是没有意义的.你可以在不继承任何东西的情况下做到这一切.(另一方面,我没有看到你如何使用继承来实现它.)例如,

class SpecialInt {
 ...
};
SpecialInt operator+ (const SpecialInt& lhs, const SpecialInt& rhs) {
  ...
}
Run Code Online (Sandbox Code Playgroud)

填写空白,你有一个解决问题的类型.你可以做SpecialInt + SpecialIntint + int,但SpecialInt + int将无法编译,完全按照你想要的.

另一方面,如果我们假装继承自int是合法的,并且我们SpecialInt派生自int,那么SpecialInt + int 就会编译.继承会导致您想要避免的确切问题.继承可以轻松避免问题.

"Ints没有任何会员功能." 好吧,他们有一大堆运营商,比如+和 - .

但这些不是成员函数.

  • +1解决实际问题(运算符重载) (4认同)

Ste*_*eel 14

如果OP真的想要理解为什么C++就是这样的话,那么他应该得到一份Stroustup的书"C++的设计和演变".它解释了在C++早期这个以及许多其他设计决策的基本原理.

  • 这是评论,而不是答案.如果在此引用相关信息而不是仅仅引用外部来源,则会得到改善. (4认同)
  • 我会做的.我有C++编程语言,但没有D&E. (2认同)

Mas*_*son 10

因为int是本机类型而不是类

编辑:将我的评论移到我的答案中.

它来自C遗产以及原始人所代表的东西.c ++中的原语只是一个字节集合,除了编译器之外几乎没有什么意义.另一方面,类有一个函数表,一旦你开始继承继承和虚拟继承路径,那么你就有了一个vtable.这些都不存在于原语中,并且通过使它呈现你会a)打破许多假定int只有8个字节的c代码和b)使程序占用更多的内存.

以另一种方式思考.int/float/char没有任何数据成员或方法.把这些原语想象成夸克 - 它们是你无法细分的构建块,你用它们来制造更大的东西(道歉,如果我的类比稍微偏离,我不知道足够的粒子物理)

  • 在创建C++时,内置类型不是类,这是一个设计决策.以另一种方式思考.int/float/char没有任何数据成员或方法.把这些原语想象成夸克 - 它们是你无法细分的构建块,你用它们来制造更大的东西(道歉,如果我的类比稍微偏离,我不知道足够的粒子物理) (4认同)
  • 这不是答案,只是对事实的重述. (2认同)

Ale*_*own 9

在c ++中强类型的int(和浮点数等)

Scott Meyer(Effective c ++有一个非常有效和强大的解决方案来解决你在c ++中强类型基类型的问题,它的工作原理如下:

强类型是一个可以在编译时解决和评估的问题,这意味着您可以在部署的应用程序中在运行时使用多种类型的序数(弱类型),并使用特殊的编译阶段来消除不适当的类型组合在编译时.

#ifdef STRONG_TYPE_COMPILE
typedef time Time
typedef distance Distance
typedef velocity Velocity
#else
typedef time float
typedef distance float
typedef velocity float
#endif
Run Code Online (Sandbox Code Playgroud)

然后,您可以定义Time,Mass,Distance与过载到适当的操作所有(唯一的)合适的运营商是类.在伪代码中:

class Time {
  public: 
  float value;
  Time operator +(Time b) {self.value + b.value;}
  Time operator -(Time b) {self.value - b.value;}
  // don't define Time*Time, Time/Time etc.
  Time operator *(float b) {self.value * b;}
  Time operator /(float b) {self.value / b;}
}

class Distance {
  public:
  float value;
  Distance operator +(Distance b) {self.value + b.value;}
  // also -, but not * or /
  Velocity operator /(Time b) {Velocity( self.value / b.value )}
}

class Velocity {
  public:
  float value;
  // appropriate operators
  Velocity(float a) : value(a) {}
}
Run Code Online (Sandbox Code Playgroud)

完成此操作后,编译器将告诉您违反上述类中编码规则的任何地方.

我会让你自己解决剩下的细节,或者买这本书.

  • "性能,代码密度":现代编译器不会完全在高优化级别内联这些类吗?我尝试了gcc.godbolt.org和-O3,它编译成完全相同的ASM指令:http://pastebin.com/sbqvXxP7 (3认同)

Pol*_*878 5

别人说的是真的...... int在C++中是一个原始的(很像C#).但是,你可以通过构建一个类来实现你想要的int:

class MyInt
{
private:
   int mInt;

public:
   explicit MyInt(int in) { mInt = in; }
   // Getters/setters etc
};
Run Code Online (Sandbox Code Playgroud)

然后,您可以继承您所需要的一切.

  • 好的,但是你要将构造函数标记为`explicit`以防止自动转换. (3认同)
  • 但是这个类的行为与int无关. (2认同)
  • 抱歉 Rocket,你想“继承” int 你必须做这项工作:) (2认同)

zeb*_*box 5

没有人提到 C++ 被设计为(主要)与 C 向后兼容,以便简化 C 编码器的升级路径,因此struct默认为所有成员 public 等。

拥有int一个您可以覆盖的基类将从根本上使该规则复杂化,并使编译器实现变得地狱般,如果您希望现有的编码人员和编译器供应商支持您的新兴语言,则可能不值得付出努力。

  • @David:不会有 vtable,因为没有虚函数。派生的 int 类的大小与原始 int 的大小相同。 (4认同)