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 intA
and intB
, which let me do intA + intA
or intB + intB
, but not intA + intB
.
我为什么要这样做?更强的类型。例如,我可以定义两个类intA和intB,我可以使用intA + intA或intB + intB,但不能使用intA + intB。
"Ints aren't classes." So what?
“int不是类”。那又怎样?
"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 -
.
Ints没有任何成员函数。它们有很多运算符,比如+和-。
19 个解决方案
#1
76
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
int
doesn't actually give a C++ programmer anything significantly new compared to having anint
member. This is primarily becauseint
doesn't have any virtual functions for the derived class to override. More seriously though, the C conversion rules are so chaotic that pretending thatint
,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成员相比,允许从int进行派生实际上不会给c++程序员带来任何明显的新东西。这主要是因为int没有任何虚函数来覆盖派生类。更严重的是,C转换规则是如此混乱,以至于假装int、short等是行为良好的普通类是行不通的。它们要么是与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++直接性能比较困难和不可能意味着很多)。
Edit: the one bit that's "wasted" in the Smalltalk implementation of smallInteger 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实现中“浪费”的那一点在C或c++中可能不需要。Smalltalk有点像Java——当您“定义一个对象”时,您实际上只是定义一个对象的指针,您必须动态地分配一个对象来指向它。你所操作的,传递给一个函数作为参数,等等,总是只是指针,而不是对象本身。
That's not how 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的方式——在这种情况下,他们将整数值直接放入通常的指针中。为了区分小整数和指针,它们强制所有对象在偶数字节边界上分配,因此LSB始终是清晰的。一个小整数总是有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.
然而,这其中大部分是必需的,因为Smalltalk是动态类型的——它必须能够通过查看值本身来推断类型,而且smallInteger基本上是使用LSB作为类型标记。由于c++是静态类型的,所以不需要从值中推断出类型,所以您可能不需要浪费这一点。
1 In The Design and Evolution of C++, §15.11.3.
1在c++的设计和演化,§15.11.3。
#2
52
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“Ints不是类”是吗?
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.
继承、多态性和封装是面向对象设计的关键。这些都不适用于序数类型。你不能从整数继承,因为它只是一堆字节,没有代码。
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.
int、chars和其他顺序类型都没有方法表,因此无法添加方法或覆盖它们,这是继承的核心。
#3
26
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 + SpecialInt
or int + int
, but SpecialInt + int
won't compile, exactly as you wanted.
填上空格,你就有了解决问题的类型。您可以执行SpecialInt + SpecialInt或int + int,但SpecialInt + int不能按照您的要求编译。
On the other hand, if we pretended that inheriting from int was legal, and our SpecialInt
derived from int
, then SpecialInt + int
would compile. Inheriting would cause the exact problem you want to avoid. Not inheriting 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 -.
Ints没有任何成员函数。它们有很多运算符,比如+和-。
Those aren't member functions though.
这些不是成员函数。
#4
14
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++早期设计决策的基本原理和许多其他设计决策。
#5
10
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没有任何数据成员或方法。把原语想象成夸克——它们是你不能细分的构建块,你可以用它们来做更大的事情(抱歉,如果我的类比有点不对,我不知道足够的粒子物理学)
#6
8
strong typing of ints (and floats etc) in 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 Meyer(有效的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
, Distance
to be classes with all (and only) the appropriate operators overloaded to the appropriate operations. In pseudo-code:
然后将时间、质量、距离定义为类,并将适当的操作符重载到适当的操作中。在伪代码:
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.
我会让你自己算出其余的细节,或者买这本书。
#7
5
What others have said is true... int
is 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.
然后你就可以继承你想要的一切。
#8
4
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 struct
defaulting to all members public etc.
没有人提到c++被设计成(大部分)与C向后兼容,以便简化C编码者的升级路径,因此struct默认为所有成员public等等。
Having int
as 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作为可以重写的基类,从根本上来说会使这个规则变得复杂,并使编译器实现变得非常糟糕,如果您希望现有的程序员和编译器供应商支持您的羽翼未丰的语言,那么这种做法可能不值得。
#9
3
In C++ the built-in types are not classes.
在c++中,内置类型不是类。
#10
3
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 typedef should be enough for that (but this has been rejected?).
不过,如果是为了更有力的打字,我理解他们的动机。甚至有人建议c++ 0x使用一种特殊类型的def就足够了(但这被拒绝了吗?)
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:
如果您自己提供了基本包装,那么可能会实现一些目标。E。g如下所示,它希望以一种合法的方式使用奇怪地重复出现的模板,并且只需要派生一个类并提供一个合适的构造函数:
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
但是,如果您建议您只定义一个新类型并重载操作符,那么您可以查看boot . operators。操作符
#11
2
Well, you don’t really need to inherit anything which hasn’t got any virtual member functions. So even if int
were a class, there would not be a plus over composition.
你不需要继承任何没有虚成员函数的东西。所以即使int是一个类,也不会有+ /复合。
So to say, virtual inheritance is the only real reason you’d need inheritance for anyway; everything else is just saving you masses of typing time. And I don’t think an int
class/type with virtual members would be the smartest thing to imagine in the C++ world. At least not for you every day int
.
也就是说,虚拟继承是你需要继承的唯一原因;其他的一切都在为你节省大量的打字时间。而且我认为在c++世界中,一个带有虚拟成员的int类/类型并不是最聪明的想法。至少不是每天都为你。
#12
1
What does it mean to inherit from an int?
从整数中继承意味着什么?
"int" has no member functions; it has no member data, it's a 32 (or 64) bit representation in memory. It doesn't have it's own vtable. All what it "has" (it doesn't really even own them) are some operators like +-/* that are really more global functions than member functions.
“int”没有成员函数;它没有成员数据,它是内存中的32位(或64位)表示。它没有自己的vtable。它所拥有的(它甚至不拥有它们)是一些操作符,比如+-/*,它们实际上比成员函数更具有全局功能。
#13
1
You can get what you want with strong typedefs. See BOOST_STRONG_TYPEDEF
你可以用强大的typedefs来得到你想要的东西。看到BOOST_STRONG_TYPEDEF
#14
0
More general than the fact that "int is primitive" is this: int
is a scalar type, while classes are aggregate types. A scalar is an atomic value, while an aggregate is something with members. Inheritance (at least as it exists in C++) only makes sense for an aggregate type, because you can't add members or methods to scalars — by definition, they don't have any members.
比“int是原语”更普遍的是:int是标量类型,而类是聚合类型。标量是一个原子值,而集合则是与成员有关的。继承(至少在c++中存在)只对聚合类型有意义,因为您不能向标量添加成员或方法——根据定义,它们没有任何成员。
#15
0
This answer is an implementation of UncleBens answer
这个答案是UncleBens答案的实现
put in Primitive.hpp
放在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);}
};
Example:
例子:
#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";
}
#16
0
Please, excuse me for my poor English.
请原谅我的英语不好。
There is a major difference between C++ correct construction like this:
C++正确的构造之间有一个主要的区别:
struct Length { double l; operator =!?:%+-*/...(); };
struct Mass { double l; operator =!?:%+-*/...(); };
and the proposed extension
和提出的扩展
struct Length : public double ;
struct Mass : public double ;
And this difference lies on the keyword this
behavior. this
is a pointer and using a pointer let few chances to use registers for computations, because in usuals processors registers does not have address. Worst, using pointer make the compiler suspicous about the fact that two pointers may designate the same memory.
这种差异取决于关键词这个行为。这是一个指针,使用一个指针使很少机会使用寄存器进行计算,因为在通常的处理器中寄存器没有地址。最糟糕的是,使用指针使编译器对两个指针可以指定相同的内存这一事实感到怀疑。
This will put an extraordinary burden on the compiler to optimize trivials ops.
这将给编译器带来极大的负担来优化小参数操作。
Another problem is on the number of bugs: reproducing exactly all the behavior of operators is absolutly error prone (for ex making constructor explicit does not forbid all implicits cases). Probability of error while building such an object is quite high. It is not equivalent to have the possibility to do something thru hard work or to have it already done.
另一个问题是bug的数量:完全复制操作符的所有行为绝对容易出错(对于ex - making构造函数显式不禁止所有的implicits情况)。构建这样一个对象时出错的概率相当高。它不等同于有可能通过努力工作或已经完成的事情来做某件事。
Compiler implementors would introduce type checking code (with maybe some errors, but compiler exactness is much better than client code, because of any bug in compiler generate countless errors cases), but main behavior of operation will remain exactly the same, with as few errors than usual.
编译器实现者会引入类型检查代码(可能会有一些错误,但是编译器的准确性要比客户端代码好得多,因为编译器中的任何错误都会产生无数的错误案例),但是操作的主要行为将保持完全相同,错误也会比通常少得多。
The proposed alternate solution (using structs during debug phase and real floats when optimized ones) is interesting but has drawbacks: it's raise the probability to have bugs only in optimized version. And debugging optimized application is much costly.
所提出的替代解决方案(在调试阶段使用结构体,在优化版本时使用真正的浮点数)很有趣,但也有缺点:它增加了只有在优化版本中出现错误的可能性。并且调试优化的应用程序是非常昂贵的。
One may implement a good proposal for @Rocketmagnet initial demand for integers types using :
可以使用以下方法对@Rocketmagnet类型的初始需求执行一个好的建议:
enum class MyIntA : long {};
auto operator=!?:%+-*/...(MyIntA);
MyIntA operator "" _A(long);
The bug level will be quite high, like using single member trick, but compiler will treat thoses types exactly like built-in integers (including register capability & optimisation), thanks for inlining.
bug级别将非常高,就像使用单个成员技巧一样,但是编译器会将这些类型完全像内置的整数(包括注册能力和优化),感谢内联。
But this trick can't be used (sadly) for floating numbers, and the nicest need is obviously real valued dimensions checking. One may not mix up apples and pears: adding length and area is a common error.
但遗憾的是,这个技巧不能用于浮点数,最需要的显然是真正有价值的维度检查。不能把苹果和梨混在一起:增加长度和面积是一个常见的错误。
Stroustrup' invocation by @Jerry is irrelevant. Virtuality is meaningful mainly for public inheritance, and the need is here toward private inheritance. Consideration around 'chaotic' C conversion rules (Does C++14 have anything not chaotic?) of basic type are also not useful : the objective is to have no default conversion rules, not to follow standard ones.
Stroustrup的调用是不相关的。虚拟性主要对公共继承有意义,这里需要的是私有继承。考虑基本类型的“混乱”C转换规则(c++ 14有什么不混乱的吗?)也没有用处:目标是没有默认的转换规则,而不是遵循标准的规则。
#17
-1
If I remember, this was the main - or one of - the main reasons C++ was not considered a true object oriented language. Java people would say: "In Java, EVERYTHING is an object" ;)
如果我记得的话,这是c++不被认为是真正面向对象语言的主要原因之一。Java人们会说:“在Java中,一切都是对象”;
#18
-3
This is related to how the items are stored in memory. An int in C++ is an integral type, as mentioned elsewhere, and is just 32 or 64 bits (a word) in memory. An object, however, is stored differently in memory. It is usually stored on the heap, and it has functionality related to polymorphism.
这与项在内存中的存储方式有关。c++中的int是一种整数类型,在其他地方提到过,在内存中只有32或64位(一个词)。然而,对象在内存中的存储方式不同。它通常存储在堆上,并且它具有与多态相关的功能。
I don't know how to explain it any better. How would you inherit from the number 4?
我不知道该如何解释它。你如何从数字4继承?
#19
-3
Why can't you inherit from int, even though you might want to?
为什么你不能从int继承呢?
Performance
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):
没有功能性的原因你不应该可以(在任意语言)继承顺序如int类型,或字符,或char *等。一些语言,例如Java和objective - c实际上提供类/对象(盒装)版本的基本类型,以满足这种需求(以及处理其他一些不愉快的后果序数类型不是对象):
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?
但是即使是Java和objective-c也保留了它们的序数类型供使用。为什么?
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.
简单的原因是性能和内存消耗。一个序数类型通常可以在一个或两个X86指令中构造、操作和按值传递,最坏的情况下只消耗几个字节。一个类通常不能-它通常使用2x或更多的内存,并且操作它的值可能需要几百个周期。
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.
应该注意的是,有相当多的语言没有序数类型,特别是像perl这样的动态语言,它几乎完全依赖于可变类型(这是另一种类型),并且共享类的一些开销。
#1
76
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
int
doesn't actually give a C++ programmer anything significantly new compared to having anint
member. This is primarily becauseint
doesn't have any virtual functions for the derived class to override. More seriously though, the C conversion rules are so chaotic that pretending thatint
,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成员相比,允许从int进行派生实际上不会给c++程序员带来任何明显的新东西。这主要是因为int没有任何虚函数来覆盖派生类。更严重的是,C转换规则是如此混乱,以至于假装int、short等是行为良好的普通类是行不通的。它们要么是与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++直接性能比较困难和不可能意味着很多)。
Edit: the one bit that's "wasted" in the Smalltalk implementation of smallInteger 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实现中“浪费”的那一点在C或c++中可能不需要。Smalltalk有点像Java——当您“定义一个对象”时,您实际上只是定义一个对象的指针,您必须动态地分配一个对象来指向它。你所操作的,传递给一个函数作为参数,等等,总是只是指针,而不是对象本身。
That's not how 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的方式——在这种情况下,他们将整数值直接放入通常的指针中。为了区分小整数和指针,它们强制所有对象在偶数字节边界上分配,因此LSB始终是清晰的。一个小整数总是有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.
然而,这其中大部分是必需的,因为Smalltalk是动态类型的——它必须能够通过查看值本身来推断类型,而且smallInteger基本上是使用LSB作为类型标记。由于c++是静态类型的,所以不需要从值中推断出类型,所以您可能不需要浪费这一点。
1 In The Design and Evolution of C++, §15.11.3.
1在c++的设计和演化,§15.11.3。
#2
52
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“Ints不是类”是吗?
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.
继承、多态性和封装是面向对象设计的关键。这些都不适用于序数类型。你不能从整数继承,因为它只是一堆字节,没有代码。
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.
int、chars和其他顺序类型都没有方法表,因此无法添加方法或覆盖它们,这是继承的核心。
#3
26
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 + SpecialInt
or int + int
, but SpecialInt + int
won't compile, exactly as you wanted.
填上空格,你就有了解决问题的类型。您可以执行SpecialInt + SpecialInt或int + int,但SpecialInt + int不能按照您的要求编译。
On the other hand, if we pretended that inheriting from int was legal, and our SpecialInt
derived from int
, then SpecialInt + int
would compile. Inheriting would cause the exact problem you want to avoid. Not inheriting 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 -.
Ints没有任何成员函数。它们有很多运算符,比如+和-。
Those aren't member functions though.
这些不是成员函数。
#4
14
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++早期设计决策的基本原理和许多其他设计决策。
#5
10
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没有任何数据成员或方法。把原语想象成夸克——它们是你不能细分的构建块,你可以用它们来做更大的事情(抱歉,如果我的类比有点不对,我不知道足够的粒子物理学)
#6
8
strong typing of ints (and floats etc) in 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 Meyer(有效的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
, Distance
to be classes with all (and only) the appropriate operators overloaded to the appropriate operations. In pseudo-code:
然后将时间、质量、距离定义为类,并将适当的操作符重载到适当的操作中。在伪代码:
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.
我会让你自己算出其余的细节,或者买这本书。
#7
5
What others have said is true... int
is 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.
然后你就可以继承你想要的一切。
#8
4
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 struct
defaulting to all members public etc.
没有人提到c++被设计成(大部分)与C向后兼容,以便简化C编码者的升级路径,因此struct默认为所有成员public等等。
Having int
as 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作为可以重写的基类,从根本上来说会使这个规则变得复杂,并使编译器实现变得非常糟糕,如果您希望现有的程序员和编译器供应商支持您的羽翼未丰的语言,那么这种做法可能不值得。
#9
3
In C++ the built-in types are not classes.
在c++中,内置类型不是类。
#10
3
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 typedef should be enough for that (but this has been rejected?).
不过,如果是为了更有力的打字,我理解他们的动机。甚至有人建议c++ 0x使用一种特殊类型的def就足够了(但这被拒绝了吗?)
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:
如果您自己提供了基本包装,那么可能会实现一些目标。E。g如下所示,它希望以一种合法的方式使用奇怪地重复出现的模板,并且只需要派生一个类并提供一个合适的构造函数:
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
但是,如果您建议您只定义一个新类型并重载操作符,那么您可以查看boot . operators。操作符
#11
2
Well, you don’t really need to inherit anything which hasn’t got any virtual member functions. So even if int
were a class, there would not be a plus over composition.
你不需要继承任何没有虚成员函数的东西。所以即使int是一个类,也不会有+ /复合。
So to say, virtual inheritance is the only real reason you’d need inheritance for anyway; everything else is just saving you masses of typing time. And I don’t think an int
class/type with virtual members would be the smartest thing to imagine in the C++ world. At least not for you every day int
.
也就是说,虚拟继承是你需要继承的唯一原因;其他的一切都在为你节省大量的打字时间。而且我认为在c++世界中,一个带有虚拟成员的int类/类型并不是最聪明的想法。至少不是每天都为你。
#12
1
What does it mean to inherit from an int?
从整数中继承意味着什么?
"int" has no member functions; it has no member data, it's a 32 (or 64) bit representation in memory. It doesn't have it's own vtable. All what it "has" (it doesn't really even own them) are some operators like +-/* that are really more global functions than member functions.
“int”没有成员函数;它没有成员数据,它是内存中的32位(或64位)表示。它没有自己的vtable。它所拥有的(它甚至不拥有它们)是一些操作符,比如+-/*,它们实际上比成员函数更具有全局功能。
#13
1
You can get what you want with strong typedefs. See BOOST_STRONG_TYPEDEF
你可以用强大的typedefs来得到你想要的东西。看到BOOST_STRONG_TYPEDEF
#14
0
More general than the fact that "int is primitive" is this: int
is a scalar type, while classes are aggregate types. A scalar is an atomic value, while an aggregate is something with members. Inheritance (at least as it exists in C++) only makes sense for an aggregate type, because you can't add members or methods to scalars — by definition, they don't have any members.
比“int是原语”更普遍的是:int是标量类型,而类是聚合类型。标量是一个原子值,而集合则是与成员有关的。继承(至少在c++中存在)只对聚合类型有意义,因为您不能向标量添加成员或方法——根据定义,它们没有任何成员。
#15
0
This answer is an implementation of UncleBens answer
这个答案是UncleBens答案的实现
put in Primitive.hpp
放在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);}
};
Example:
例子:
#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";
}
#16
0
Please, excuse me for my poor English.
请原谅我的英语不好。
There is a major difference between C++ correct construction like this:
C++正确的构造之间有一个主要的区别:
struct Length { double l; operator =!?:%+-*/...(); };
struct Mass { double l; operator =!?:%+-*/...(); };
and the proposed extension
和提出的扩展
struct Length : public double ;
struct Mass : public double ;
And this difference lies on the keyword this
behavior. this
is a pointer and using a pointer let few chances to use registers for computations, because in usuals processors registers does not have address. Worst, using pointer make the compiler suspicous about the fact that two pointers may designate the same memory.
这种差异取决于关键词这个行为。这是一个指针,使用一个指针使很少机会使用寄存器进行计算,因为在通常的处理器中寄存器没有地址。最糟糕的是,使用指针使编译器对两个指针可以指定相同的内存这一事实感到怀疑。
This will put an extraordinary burden on the compiler to optimize trivials ops.
这将给编译器带来极大的负担来优化小参数操作。
Another problem is on the number of bugs: reproducing exactly all the behavior of operators is absolutly error prone (for ex making constructor explicit does not forbid all implicits cases). Probability of error while building such an object is quite high. It is not equivalent to have the possibility to do something thru hard work or to have it already done.
另一个问题是bug的数量:完全复制操作符的所有行为绝对容易出错(对于ex - making构造函数显式不禁止所有的implicits情况)。构建这样一个对象时出错的概率相当高。它不等同于有可能通过努力工作或已经完成的事情来做某件事。
Compiler implementors would introduce type checking code (with maybe some errors, but compiler exactness is much better than client code, because of any bug in compiler generate countless errors cases), but main behavior of operation will remain exactly the same, with as few errors than usual.
编译器实现者会引入类型检查代码(可能会有一些错误,但是编译器的准确性要比客户端代码好得多,因为编译器中的任何错误都会产生无数的错误案例),但是操作的主要行为将保持完全相同,错误也会比通常少得多。
The proposed alternate solution (using structs during debug phase and real floats when optimized ones) is interesting but has drawbacks: it's raise the probability to have bugs only in optimized version. And debugging optimized application is much costly.
所提出的替代解决方案(在调试阶段使用结构体,在优化版本时使用真正的浮点数)很有趣,但也有缺点:它增加了只有在优化版本中出现错误的可能性。并且调试优化的应用程序是非常昂贵的。
One may implement a good proposal for @Rocketmagnet initial demand for integers types using :
可以使用以下方法对@Rocketmagnet类型的初始需求执行一个好的建议:
enum class MyIntA : long {};
auto operator=!?:%+-*/...(MyIntA);
MyIntA operator "" _A(long);
The bug level will be quite high, like using single member trick, but compiler will treat thoses types exactly like built-in integers (including register capability & optimisation), thanks for inlining.
bug级别将非常高,就像使用单个成员技巧一样,但是编译器会将这些类型完全像内置的整数(包括注册能力和优化),感谢内联。
But this trick can't be used (sadly) for floating numbers, and the nicest need is obviously real valued dimensions checking. One may not mix up apples and pears: adding length and area is a common error.
但遗憾的是,这个技巧不能用于浮点数,最需要的显然是真正有价值的维度检查。不能把苹果和梨混在一起:增加长度和面积是一个常见的错误。
Stroustrup' invocation by @Jerry is irrelevant. Virtuality is meaningful mainly for public inheritance, and the need is here toward private inheritance. Consideration around 'chaotic' C conversion rules (Does C++14 have anything not chaotic?) of basic type are also not useful : the objective is to have no default conversion rules, not to follow standard ones.
Stroustrup的调用是不相关的。虚拟性主要对公共继承有意义,这里需要的是私有继承。考虑基本类型的“混乱”C转换规则(c++ 14有什么不混乱的吗?)也没有用处:目标是没有默认的转换规则,而不是遵循标准的规则。
#17
-1
If I remember, this was the main - or one of - the main reasons C++ was not considered a true object oriented language. Java people would say: "In Java, EVERYTHING is an object" ;)
如果我记得的话,这是c++不被认为是真正面向对象语言的主要原因之一。Java人们会说:“在Java中,一切都是对象”;
#18
-3
This is related to how the items are stored in memory. An int in C++ is an integral type, as mentioned elsewhere, and is just 32 or 64 bits (a word) in memory. An object, however, is stored differently in memory. It is usually stored on the heap, and it has functionality related to polymorphism.
这与项在内存中的存储方式有关。c++中的int是一种整数类型,在其他地方提到过,在内存中只有32或64位(一个词)。然而,对象在内存中的存储方式不同。它通常存储在堆上,并且它具有与多态相关的功能。
I don't know how to explain it any better. How would you inherit from the number 4?
我不知道该如何解释它。你如何从数字4继承?
#19
-3
Why can't you inherit from int, even though you might want to?
为什么你不能从int继承呢?
Performance
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):
没有功能性的原因你不应该可以(在任意语言)继承顺序如int类型,或字符,或char *等。一些语言,例如Java和objective - c实际上提供类/对象(盒装)版本的基本类型,以满足这种需求(以及处理其他一些不愉快的后果序数类型不是对象):
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?
但是即使是Java和objective-c也保留了它们的序数类型供使用。为什么?
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.
简单的原因是性能和内存消耗。一个序数类型通常可以在一个或两个X86指令中构造、操作和按值传递,最坏的情况下只消耗几个字节。一个类通常不能-它通常使用2x或更多的内存,并且操作它的值可能需要几百个周期。
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.
应该注意的是,有相当多的语言没有序数类型,特别是像perl这样的动态语言,它几乎完全依赖于可变类型(这是另一种类型),并且共享类的一些开销。