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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2143020/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:14:56  来源:igfitidea点击:

Why can't I inherit from int in C++?

c++inheritanceintegerlanguage-designstrong-typing

提问by Rocketmagnet

I'd love to be able to do this:

我希望能够做到这一点:

class myInt : public int
{

};

Why can't I?

为什么我不能?

Why would I want to? Stronger typing. For example, I could define two classes intAand intB, which let me do intA + intAor intB + intB, but not intA + intB.

我为什么要?更强的打字。例如,我可以定义两个类intAand intB,它们让我可以做intA + intAor intB + intB,但不能做intA + intB

"Ints aren't classes." So what?

“整数不是类。” 所以呢?

"Ints don't have any member data." Yes they do, they have 32 bits, or whatever.

“Ints 没有任何成员数据。” 是的,他们有,他们有 32 位,或其他。

"Ints don't have any member functions." Well, they have a whole bunch of operators like +and -.

“整数没有任何成员函数。” 好吧,他们有一大堆像+and这样的运算符-

回答by Jerry Coffin

Neil's comment is pretty accurate. Bjarne mentioned considering and rejecting this exact possibility1:

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

The initializer syntax used to be illegal for built-in types. To allow it, I introduced the notion that built-in types have constructors and destructors. For example:

int a(1);    // pre-2.1 error, now initializes a to 1

I considered extending this notion to allow derivation from built-in classes and explicit declaration of built-in operators for built-in types. However, I restrained myself.

Allowing derivation from an intdoesn't actually give a C++ programmer anything significantly new compared to having an intmember. This is primarily because intdoesn't have any virtual functions for the derived class to override. More seriously though, the C conversion rules are so chaotic that pretending that int, short, etc., are well-behaved ordinary classes is not going to work. They are either C compatible, or they obey the relatively well-behaved C++ rules for classes, but not both.

对于内置类型,初始化语法曾经是非法的。为此,我引入了内置类型具有构造函数和析构函数的概念。例如:

int a(1);    // pre-2.1 error, now initializes a to 1

我考虑扩展这个概念以允许从内置类派生并显式声明内置类型的内置运算符。不过,我还是克制了自己。

int与拥有int成员相比,允许从 an 派生实际上并没有给 C++ 程序员任何显着的新东西。这主要是因为int派生类没有任何要覆盖的虚函数。更严重的是虽然,C转换规则是如此混乱是假装intshort等等,都是乖巧普通班是不会工作。它们要么与 C 兼容,要么遵守相对良好的 C++ 类规则,但不能两者兼而有之。

As far as the comment the performance justifies not making int a class, it's (at least mostly) false. In Smalltalk all types are classes -- but nearly all implementations of Smalltalk have optimizations so the implementation can be essentially identical to how you'd make a non-class type work. For example, the smallInteger class is represents a 15-bit integer, and the '+' message is hard-coded into the virtual machine, so even though you can derive from smallInteger, it still gives performance similar to a built-in type (though Smalltalk is enough different from C++ that direct performance comparisons are difficult and unlikely to mean much).

至于性能证明不将 int 变成一个类的评论,它(至少大部分)是错误的。在 Smalltalk 中,所有类型都是类——但几乎所有 Smalltalk 的实现都有优化,因此实现可以与您使非类类型工作的方式基本相同。例如,smallInteger 类是表示一个 15 位整数,并且“+”消息被硬编码到虚拟机中,因此即使您可以从 smallInteger 派生,它仍然提供类似于内置类型的性能(尽管 Smalltalk 与 C++ 有很大的不同,直接的性能比较是困难的,也不太可能有太大意义)。

The one bit that's "wasted" in the Smalltalk implementation of smallInteger (the reason it only represents 15 bits instead of 16) probably wouldn't be needed in C or C++. Smalltalk is a bit like Java -- when you "define an object" you're really just defining a pointer to an object, and you have to dynamically allocate an object for it to point at. What you manipulate, pass to a function as a parameter, etc., is always just the pointer, not the object itself.

在 smallInteger 的 Smalltalk 实现中“浪费”的一位(因为它只代表 15 位而不是 16 位)在 C 或 C++ 中可能不需要。Smalltalk 有点像 Java —— 当你“定义一个对象”时,你实际上只是定义了一个指向一个对象的指针,你必须动态分配一个对象来指向它。您所操作的、作为参数传递给函数的等等,始终只是指针,而不是对象本身。

That's nothow smallInteger is implemented though -- in its case, they put the integer value directly into what would normally be the pointer. To distinguish between a smallInteger and a pointer, they force all objects to be allocated at even byte boundaries, so the LSB is always clear. A smallInteger always has the LSB set.

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

Most of this is necessary, however, because Smalltalk is dynamically typed -- it has to be able to deduce the type by looking at the value itself, and smallInteger is basically using that LSB as a type-tag. Given that C++ is statically typed, there's never a need to deduce the type from the value, so you probably wouldn't need to "waste" that bit on a type-tag.

然而,其中大部分是必需的,因为 Smalltalk 是动态类型的——它必须能够通过查看值本身来推断类型,而 smallInteger 基本上使用该 LSB 作为类型标记。鉴于 C++ 是静态类型的,永远不需要从值中推导出类型,因此您可能不需要在类型标记上“浪费”该位。



1. In The Design and Evolution of C++, §15.11.3.

1. 在C++ 的设计和演化中,第 15.11.3 节。

回答by 3Dave

Int is an ordinal type, not a class. Why would you want to?

Int 是一个序数类型,而不是一个类。你为什么要?

If you need to add functionality to "int", consider building an aggregate class which has an integer field, and methods that expose whatever additional capabilities that you require.

如果您需要向“int”添加功能,请考虑构建一个具有整数字段的聚合类,以及公开您需要的任何附加功能的方法。

Update

更新

@OP "Ints aren't classes" so?

@OP 整数不是类”所以?

Inheritance, polymorphism and encapsulation are keystones of object oriented design. None of these things apply to ordinal types. You can't inherit from an int because it's just a bunch of bytes and has no code.

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

Ints, chars, and other ordinal types do not have method tables, so there's no way to add methods or override them, which is really the heart of inheritance.

整数、字符和其他序数类型没有方法表,因此无法添加或覆盖它们,这确实是继承的核心。

回答by jalf

Why would I want to? Stronger typing. For example, I could define two classes intA and intB, which let me do intA+intA or intB+intB, but not intA+intB.

我为什么要?更强的打字。例如,我可以定义两个类 intA 和 intB,它们让我可以做 intA+intA 或 intB+intB,但不能做 intA+intB。

That makes no sense. You can do all that without inheriting from anything. (And on the other hand, I don't see how you could possibly achieve it using inheritance.) For example,

这是没有意义的。你可以在不继承任何东西的情况下完成所有这些。(另一方面,我不知道您如何使用继承来实现它。)例如,

class SpecialInt {
 ...
};
SpecialInt operator+ (const SpecialInt& lhs, const SpecialInt& rhs) {
  ...
}

Fill in the blanks, and you have a type that solves your problem. You can do SpecialInt + SpecialIntor int + int, but SpecialInt + intwon't compile, exactly as you wanted.

填空,你就有了一个能解决你问题的类型。您可以完全按照您的意愿执行SpecialInt + SpecialIntor int + int,但SpecialInt + int不会编译。

On the other hand, if we pretended that inheriting from int was legal, and our SpecialIntderived from int, then SpecialInt + intwouldcompile. Inheriting would cause the exact problem you want to avoid. Notinheriting avoids the problem easily.

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

"Ints don't have any member functions." Well, they have a whole bunch of operators like + and -.

“整数没有任何成员函数。” 好吧,他们有一大堆像 + 和 - 这样的运算符。

Those aren't member functions though.

这些不是成员函数。

回答by Stephen C. Steel

If the OP really wants to understand WHY C++ is the way it is, then he should get a hold of a copy of Stroustup's book "The Design and Evolution of C++". It explains the rationale for this and many other design decisions in the early days of C++.

如果 OP 真的想了解为什么 C++ 是这样的,那么他应该拿到 Stroustup 的书“C++ 的设计和演变”的副本。它解释了在 C++ 早期做出这一决定和许多其他设计决定的基本原理。

回答by Alex Brown

strong typing of ints (and floats etc) in c++

在 C++ 中强类型的整数(和浮点数等)

Scott Meyer(Effective c++has a very effective and powerful solution to your problem of doing strong typing of base types in c++, and it works like this:

Scott MeyerEffective c++有一个非常有效和强大的解决方案来解决你在 c++ 中对基类型进行强类型化的问题,它的工作原理是这样的:

Strong typing is a problem that can be addressed and evaluated at compile time, which means you can use the ordinals (weak typing) for multiple types at run-time in deployed apps, and use a special compile phase to iron out inappropriate combinations of types at compile time.

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

#ifdef STRONG_TYPE_COMPILE
typedef time Time
typedef distance Distance
typedef velocity Velocity
#else
typedef time float
typedef distance float
typedef velocity float
#endif

You then define your Time, Mass, Distanceto be classes with all (and only) the appropriate operators overloaded to the appropriate operations. In pseudo-code:

然后,您将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) {}
}

Once this is done, your compiler will tell you any places you have violated the rules encoded in the above classes.

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

I'll let you work out the rest of the details yourself, or buy the book.

我会让你自己解决其余的细节,或者买这本书。

回答by Mason

Because int is a native type and not a class

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

Edit: moving my comments into my answer.

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

It comes from the C heritage and what, exactly, primitives represent. A primitive in c++ is just a collection of bytes that have little meaning except to the compiler. A class, on the other hand, has a function table, and once you start going down the inheritance and virtual inheritance path, then you have a vtable. None of that is present in a primitive, and by making it present you would a) break a lot of c code that assumes an int is 8 bytes only and b) make programs take up a lot more memory.

它来自 C 语言遗产以及原语所代表的东西。C++ 中的原语只是一组字节,除了对编译器没有任何意义。另一方面,一个类有一个函数表,一旦你开始沿着继承和虚拟继承的路径走下去,那么你就有了一个 vtable。这些都不存在于原语中,通过使其存在,您将 a) 破坏大量假设 int 只有 8 个字节的 c 代码,并且 b) 使程序占用更多内存。

Think about it another way. int/float/char don't have any data members or methods. Think of the primitives as quarks - they're the building blocks that you can't subdivide, you use them to make bigger things (apologies if my analogy is a little off, I don't know enough particle physics)

换一种方式想一想。int/float/char 没有任何数据成员或方法。把基元想象成夸克——它们是你无法细分的积木,你可以用它们来制造更大的东西(抱歉,如果我的比喻有点不对,我对粒子物理学的了解不够)

回答by zebrabox

No one has mentioned that C++ was designed to have (mostly) backwards compatibility with C, so as to ease the upgrade path for C coders hence structdefaulting to all members public etc.

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

Having intas a base class that you could override would fundamentally complicate that rule no end and make the compiler implementation hellish which if you want existing coders and compiler vendors to support your fledgling language was probably not worth the effort.

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

回答by Polaris878

What others have said is true... intis a primitive in C++ (much like C#). However, you can achieve what you wanted by just building a class around int:

其他人所说的是真的……int是 C++ 中的一个原语(很像 C#)。但是,您可以通过围绕int以下内容构建一个类来实现您想要的:

class MyInt
{
private:
   int mInt;

public:
   explicit MyInt(int in) { mInt = in; }
   // Getters/setters etc
};

You can then inherit from that all you jolly want.

然后,您可以从中继承您想要的一切。

回答by Trent

In C++ the built-in types are not classes.

在 C++ 中,内置类型不是类。

回答by UncleBens

As others I saying, can't be done since int is a primitive type.

正如我所说的其他人,无法完成,因为 int 是原始类型。

I understand the motivation, though, if it is for stronger typing. It has even been proposed for C++0x that a special kind of typedefshould be enough for that (but this has been rejected?).

不过,如果是为了更强大的打字,我理解动机。甚至有人提议为 C++0x 使用一种特殊类型的 typedef就足够了(但这已被拒绝?)。

Perhaps something could be achieved, if you provided the base wrapper yourself. E.g something like the following, which hopefully uses curiously recurring templates in a legal manner, and requires only deriving a class and providing a suitable constructor:

如果您自己提供基本包装器,也许可以实现某些目标。例如像下面这样的东西,它希望以合法的方式使用奇怪的重复模板,并且只需要派生一个类并提供合适的构造函数:

template <class Child, class T>
class Wrapper
{
    T n;
public:
    Wrapper(T n = T()): n(n) {}
    T& value() { return n; }
    T value() const { return n; }
    Child operator+= (Wrapper other) { return Child(n += other.n); }
    //... many other operators
};

template <class Child, class T>
Child operator+(Wrapper<Child, T> lhv, Wrapper<Child, T> rhv)
{
    return Wrapper<Child, T>(lhv) += rhv;
}

//Make two different kinds of "int"'s

struct IntA : public Wrapper<IntA, int>
{
    IntA(int n = 0): Wrapper<IntA, int>(n) {}
};

struct IntB : public Wrapper<IntB, int>
{
    IntB(int n = 0): Wrapper<IntB, int>(n) {}
};

#include <iostream>

int main()
{
    IntA a1 = 1, a2 = 2, a3;
    IntB b1 = 1, b2 = 2, b3;
    a3 = a1 + a2;
    b3 = b1 + b2;
    //a1 + b1;  //bingo
    //a1 = b1; //bingo
    a1 += a2;

    std::cout << a1.value() << ' ' << b3.value() << '\n';
}

But if you take the advice that you should just define a new type and overload the operators, you might take a look at Boost.Operators

但是如果你建议你应该只定义一个新类型并重载运算符,你可以看看Boost.Operators