为什么我不能从C中inheritanceint?

我希望能够做到这一点:

class myInt : public int { }; 

为什么我不能?

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

“我不是class级。” 所以呢?

“Ints没有任何会员资料。” 是的,他们有32位,或者其他什么。

“Ints没有任何成员职能”。 那么,他们有一大堆运营商像+-

尼尔的评论是相当准确的。 Bjarne提到考虑和拒绝这个确切的可能性1

初始化器语法对于内置types是非法的。 为了允许它,我介绍了内置types具有构造函数和析构函数的概念。 例如:

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

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

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

至于评论的performancecertificate不作为一个类,它(至less大部分)是错误的。 在Smalltalk中,所有types都是类,但是几乎所有Smalltalk的实现都有优化,所以实现可以基本上与您创build非类types的工作相同。 例如,smallInteger类表示一个15位的整数,“+”消息被硬编码到虚拟机中,所以尽pipe你可以从smallInteger派生,但它仍然具有类似于内置types的性能尽pipeSmalltalk与C ++的不同之处在于,直接进行性能比较是困难的并且不太可能意味着什么)。

编辑:在SmallInteger的Smalltalk实现中“浪费”的一点在C或C ++中可能不需要。 Smalltalk有点像Java–当你“定义一个对象”时,你实际上只是定义了一个指向对象的指针,而且你必须dynamic地分配一个对象来指向它。 你操作的东西,作为parameter passing给函数等,总是只是指针,而不是对象本身。

并不是 smallInteger实现的方式 – 在这种情况下,它们将整数值直接放到通常是指针的地方。 为了区分一个smallInteger和一个指针,它们强制所有对象在偶数字节边界处分配,所以LSB总是清零。 一个smallInteger总是有LSB设置。

然而,大多数情况下这是必须的,因为Smalltalk是dynamictypes的 – 它必须能够通过查看值本身来推断types,而smallInteger基本上是使用该LSB作为types标记。 由于C ++是静态types的,所以从来没有必要从这个值中推导出types,所以你可能不需要浪费那么多。

1C ++的devise和演变中 ,§15.11.3。

Int是序数types,而不是类。 你为什么要?

如果需要为“int”添加function,请考虑构build一个具有整数字段的聚合类,以及公开所需的其他function的方法。

更新

@OP “Ints不是类”是这样吗?

inheritance ,多态和封装是面向对象devise的基石。 这些东西都不适用于序数types。 你不能从intinheritance,因为它只是一堆字节而没有代码。

Ints,字符和其他序数types没有方法表 ,所以没有办法添加方法或覆盖它们,这是inheritance的核心。

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

这是没有意义的。 你可以做所有这些,而不需要inheritance任何东西。 (另一方面,我不知道如何使用inheritance来实现它。)例如,

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

填空,你有一个解决你的问题的types。 你可以做SpecialInt + SpecialInt或者int + int ,但SpecialInt + int不会完全按照你的想法编译。

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

“Ints没有任何成员职能”。 那么,他们有一大堆运营商像+和 – 。

那些不是成员函数。

如果OP真的想明白为什么C ++是这样的话,那么他应该抓住Stroustup的书“The Design and Evolution of C ++”的副本。 它解释了在C ++早期这个和其他许多devise决定的基本原理。

因为int是一个本地types而不是一个类

编辑:移动我的意见到我的答案。

它来源于C遗产,而原始人所代表的是什么。 c ++中的一个原语只是一个除了编译器之外没有什么意义的字节集合。 另一方面,类有一个函数表,一旦你开始继续下去的inheritance和虚拟inheritancepath,那么你有一个虚拟表。 这些都不是在原语中存在的,通过使它出现,你会打破许多假设int只有8个字节的c代码,并且b)使程序占用更多的内存。

想想另一种方式。 int / float / char没有任何数据成员或方法。 把原始图看成是夸克 – 它们是你不能细分的基石,你用它来做更大的事情(如果我的比喻有点关,我不知道足够的粒子物理)

ints(和浮点数等)在c ++中的强键入

Scott Meyer ( 有效的c ++有一个非常有效和强大的解决scheme,你在c ++中强types的基types的问题,它的工作原理是这样的:

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

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

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

一旦完成,您的编译器会告诉您任何违反上述类中编码规则的地方。

我会让你自己弄清其余的细节,或者购买这本书。

其他人说的是真的… int是C ++中的一个原语(很像C#)。 但是,你可以通过围绕int构build一个类来达到你想要的效果:

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

那么你可以inheritance你所需要的一切。

没有人提到过C ++被devise成具有(大部分)与C的向后兼容性,从而简化了C编码器的升级path,因此struct默认为所有成员public。

int作为一个基类,你可以重载将从根本上使这个规则复杂化,并使编译器实现,如果你希望现有的编码器和编译器厂商支持你的羽翼未丰的语言可能是不值得的努力。

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

正如其他人所说,由于int是一种原始types,因此无法完成。

不过,我明白这个动机,如果是为了更强的打字。 甚至有人build议使用C ++ 0x,一种特殊的typedef应该足够了(但是这已经被拒绝了)。

如果你自己提供了基本的包装,也许可以实现一些东西。 比如像下面这样的东西,希望以合法的方式使用奇怪的循环模板,并且只需要派生一个类并提供一个合适的构造函数:

 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'; } 

但是,如果你采取build议,你应该只是定义一个新types,并重载运营商,你可以看看Boost.Operators

那么,你并不需要inheritance任何没有任何虚拟成员函数的东西。 所以,即使int 一个类,也不会有超过构成的加。

所以说,虚拟inheritance是你无论如何需要inheritance的唯一真正原因; 一切都只是节省你打字时间的大量。 我不认为在虚拟成员中使用int类/types是在C ++世界中想象的最聪明的事情。 至less不适合你每天int

从intinheritance是什么意思?

“int”没有成员函数; 它没有成员数据,它在内存中是一个32位(或64位)的表示。 它没有它自己的vtable。 所有“拥有”(甚至不拥有它们)都是一些运算符,比如+ – / *,这些运算符实际上比成员函数更具全局性。

你可以得到你想要的强typedefs。 请参阅BOOST_STRONG_TYPEDEF

比“int is primitive”这个事实更普遍的是: int是一个标量types,而类是聚合types。 标量是一个primefaces值,而聚合是与成员的东西。 inheritance(至less在C ++中存在)只对聚合types有意义,因为不能将成员或方法添加到标量 – 根据定义,它们没有任何成员。

这个答案是UncleBens答案的一个实现

放在Primitive.hpp中

 #pragma once template<typename T, typename Child> class Primitive { protected: T value; public: // we must type cast to child to so // a += 3 += 5 ... and etc.. work the same way // as on primitives Child &childRef(){ return *((Child*)this); } // you can overload to give a default value if you want Primitive(){} explicit Primitive(T v):value(v){} T get(){ return value; } #define OP(op) Child &operator op(Child const &v){\ value op v.value; \ return childRef(); \ } // all with equals OP(+=) OP(-=) OP(*=) OP(/=) OP(<<=) OP(>>=) OP(|=) OP(^=) OP(&=) OP(%=) #undef OP #define OP(p) Child operator p(Child const &v){\ Child other = childRef();\ other p ## = v;\ return other;\ } OP(+) OP(-) OP(*) OP(/) OP(<<) OP(>>) OP(|) OP(^) OP(&) OP(%) #undef OP #define OP(p) bool operator p(Child const &v){\ return value p v.value;\ } OP(&&) OP(||) OP(<) OP(<=) OP(>) OP(>=) OP(==) OP(!=) #undef OP Child operator +(){return Child(value);} Child operator -(){return Child(-value);} Child &operator ++(){++value; return childRef();} Child operator ++(int){ Child ret(value); ++value; return childRef(); } Child operator --(int){ Child ret(value); --value; return childRef(); } bool operator!(){return !value;} Child operator~(){return Child(~value);} }; 

例:

 #include "Primitive.hpp" #include <iostream> using namespace std; class Integer : public Primitive<int, Integer> { public: Integer(){} Integer(int a):Primitive<int, Integer>(a) {} }; int main(){ Integer a(3); Integer b(8); a += b; cout << a.get() << "\n"; Integer c; c = a + b; cout << c.get() << "\n"; cout << (a > b) << "\n"; cout << (!b) << " " << (!!b) << "\n"; } 

请原谅我可怜的英语。

C ++正确的构造之间有一个主要的区别:

 struct Length { double l; operator =!?:%+-*/...(); }; struct Mass { double l; operator =!?:%+-*/...(); }; 

和build议的扩展

 struct Length : public double ; struct Mass : public double ; 

而这个区别在于关键字this行为。 this是一个指针,使用一个指针让很less机会使用寄存器进行计算,因为在通常的处理器寄存器中没有地址。 最糟糕的是,使用指针使得编译器怀疑两个指针可能指定相同内存的事实。

这会给编译器带来非常大的负担,以优化简单操作。

另一个问题是bug的数量:完全重现操作符的所有行为是非常容易出错的(因为前者使得构造函数显式不禁止所有含义的情况)。 build造这样一个物体时发生错误的可能性相当高。 有可能通过努力工作或已经完成的事情是不相等的。

编译器实现者会引入types检查代码(可能有一些错误,但编译器的正确性比客户端代码好得多,因为编译器中的任何错误都会产生无数的错误情况),但是主要的操作行为将保持完全一样,只有很less的错误比平常。

所提出的替代解决scheme(在debugging阶段使用结构和优化后的实际浮点数)很有趣,但有缺点:只会在优化版本中产生错误。 而且debugging优化的应用程序是非常昂贵的。

可以使用以下方法为@Rocketmagnet整数types的初始需求实现一个好的build议:

 enum class MyIntA : long {}; auto operator=!?:%+-*/...(MyIntA); MyIntA operator "" _A(long); 

错误级别会非常高,就像使用单一成员技巧一样,但是编译器会像内置整数(包括注册能力和优化)一样对待types,感谢内联。

但是这个技巧不能用于(浮躁的)浮动数字,最好的需求显然是真正有价值的维度检查。 一个可能不会混淆苹果和梨:增加长度和面积是一个常见的错误。

@Jerry调用Stroustrup'是无关紧要的。 虚拟性主要对于公共inheritance是有意义的,在这里需要私有inheritance。 围绕“混乱”的C语言转换规则(C ++ 14有没有什么混乱的东西)考虑基本types也是没有用的:目标是没有默认的转换规则,不遵循标准规则。

如果我记得,这是C ++不被视为真正的面向对象语言的主要原因之一。 Java人会说:“在Java中,一切都是一个对象”;)

这与项目如何存储在内存中有关。 正如其他地方所提到的,C ++中的int是一个整型,在内存中只有32或64位(一个字)。 然而,一个对象在内存中的存储方式是不同的。 它通常存储在堆中,并且具有与多态相关的function。

我不知道如何解释它。 你将如何inheritance数字4?

Why can't you inherit from int, even though you might want to?

性能

There's no functional reason why you shouldn't be able (in an arbitrary language) inherit from ordinal types such as int, or char, or char* etc. Some languages such as Java and Objective-C actually provide class/object (boxed) versions of the base type, in order to satisfy this need (as well as to deal with some other unpleasant consequences of ordinal types not being objects):

 language ordinal type boxed type, c++ int ? java int Integer objective-c int NSNumber 

But even Java and objective-c preserve their ordinal types for use… why?

The simple reasons are performance and memory consumption. An ordinal type can be typically be constructed, operated upon, and passed by value in just one or two X86 instructions, and only consumes a few bytes at worst. A class typically cannot – it often uses 2x or more as much memory, and manipulating its value may take many hundreds of cycles.

This means programmers who understand this will typically use the ordinal types to implement performance or memory usage sensitive code, and will demand that language developers support the base types.

It should be noted that quite a few languages do not have ordinal types, in particular the dynamic languages such as perl , which relies almost entirely on a variadic type, which is something else altogether, and shares some of the overhead of classes.