C ++에서 int에서 상속 할 수없는 이유는 무엇입니까?
나는 이것을 할 수 있기를 바랍니다.
class myInt : public int
{
};
왜 안돼?
내가 원하는 이유는 무엇입니까? 더 강력한 타이핑. 예를 들어, 나는 두 개의 클래스를 정의 할 수 있습니다 intA
와 intB
나하자있는, intA + intA
또는 intB + intB
아니지만 intA + intB
.
"Int는 클래스가 아닙니다." 그래서 뭐?
"Ints에는 멤버 데이터가 없습니다." 예, 그들은 32 비트를 가지고 있습니다.
"Int에는 멤버 함수가 없습니다." 글쎄, 그들은 +
및 같은 많은 연산자를 가지고 있습니다 -
.
Neil의 의견은 매우 정확합니다. Bjarne은이 정확한 가능성을 고려하고 거부한다고 언급했습니다 1 :
이니셜 라이저 구문은 내장 유형에 적합하지 않았습니다. 이를 허용하기 위해 기본 제공 유형에 생성자와 소멸자가 있다는 개념을 도입했습니다. 예를 들면 :
int a(1); // pre-2.1 error, now initializes a to 1
이 개념을 확장하여 기본 제공 클래스에서 파생하고 기본 제공 형식에 대한 기본 제공 연산자를 명시 적으로 선언 할 수 있도록 고려했습니다. 그러나 나는 자제했다.
에서 파생을 허용
int
한다고해서 실제로 C ++ 프로그래머에게int
멤버 가있는 것에 비해 상당히 새로운 것을주지는 않습니다 . 이는 주로int
파생 클래스가 재정의 할 가상 함수가 없기 때문 입니다. 더 심각하게, 그래도 C의 변환 규칙은 척 너무 혼란있다int
,short
일하지 않을 등 일반 수업을 잘 행동하고 있습니다. C와 호환되거나 클래스에 대해 비교적 잘 작동하는 C ++ 규칙을 따르지만 둘다는 아닙니다.
주석이 int를 클래스로 만들지 않는 것을 정당화하는 한 (적어도 대부분) 거짓입니다. 스몰 토크에서 모든 유형은 클래스이지만 거의 모든 스몰 토크 구현에는 최적화가 있으므로 구현은 기본적으로 비 클래스 유형의 작동 방식과 동일 할 수 있습니다. 예를 들어 smallInteger 클래스는 15 비트 정수를 나타내고 '+'메시지는 가상 머신에 하드 코딩되어 있으므로 smallInteger에서 파생 될 수 있지만 여전히 기본 제공 유형과 유사한 성능을 제공합니다 ( 스몰 토크는 C ++와는 충분히 다르기 때문에 직접적인 성능 비교가 어렵고 의미가 없을 것 같습니다.)
smallInteger의 Smalltalk 구현에서 "낭비 된"1 비트 (16 비트 대신 15 비트 만 나타내는 이유)는 C 또는 C ++에서 필요하지 않을 것입니다. 스몰 토크는 자바와 비슷합니다. "객체를 정의"할 때 실제로는 객체에 대한 포인터를 정의하는 것이며, 객체가 가리키는 객체를 동적으로 할당해야합니다. 사용자가 조작하고 매개 변수로 함수에 전달하는 것은 항상 객체 자체가 아니라 포인터 일뿐입니다.
그건 하지 smallInteger이 있지만 구현 방법 - 케이스에, 그들은 직접 일반적으로 포인터 일 것입니다 무슨에 정수 값을 넣습니다. smallInteger와 포인터를 구별하기 위해 모든 객체가 짝수 바이트 경계에 할당되도록 강제하므로 LSB는 항상 명확합니다. smallInteger에는 항상 LSB 세트가 있습니다.
그러나 Smalltalk는 동적으로 입력되므로 값 자체를보고 유형을 추론 할 수 있어야하며 smallInteger는 기본적으로 해당 LSB를 유형 태그로 사용합니다. C ++가 정적으로 형식화되어 있으므로 값에서 형식을 추론 할 필요가 없으므로 형식 태그에서 해당 비트를 "낭비"할 필요가 없을 것입니다.
1. The Design and Evolution of C ++ , §15.11.3.
Int는 클래스가 아닌 서수 유형입니다. 왜 그러고 싶니?
"int"에 기능을 추가해야하는 경우 정수 필드가있는 집계 클래스와 필요한 추가 기능을 노출하는 메서드를 구축하는 것이 좋습니다.
최신 정보
@OP "Ints는 클래스가 아닙니다"그래서?
상속 , 다형성 및 캡슐화는 객체 지향 디자인의 핵심입니다 . 이들 중 어느 것도 서수 유형에 적용되지 않습니다. 그것은 단지 바이트이고 코드가 없기 때문에 int에서 상속 할 수 없습니다.
Int, chars 및 기타 서수 유형에는 메소드 테이블 이 없으므로 실제로 상속의 핵심 인 메소드를 추가하거나 재정의 할 방법이 없습니다.
내가 원하는 이유는 무엇입니까? 더 강력한 타이핑. 예를 들어, intA + intA 또는 intB + intB를 수행 할 수 있지만 intA + intB가 아닌 두 개의 클래스 intA 및 intB를 정의 할 수 있습니다.
말이 안 돼. 아무것도 상속하지 않고도 모든 것을 할 수 있습니다. (반면에 상속을 사용하여 어떻게 달성 할 수 있을지 모르겠습니다.) 예를 들어,
class SpecialInt {
...
};
SpecialInt operator+ (const SpecialInt& lhs, const SpecialInt& rhs) {
...
}
빈칸을 채우면 문제를 해결하는 유형이 있습니다. 당신은 할 수 있습니다 SpecialInt + SpecialInt
또는 int + int
, 그러나 SpecialInt + int
당신이 원하는대로 정확하게 컴파일되지 않습니다.
한편, 우리는 INT에서 상속하는 법률이었다 척, 우리는 SpecialInt
에서 파생 된 int
후, SpecialInt + int
것이다 컴파일합니다. 상속하면 피하려는 정확한 문제가 발생합니다. 아니 쉽게을 피에게 문제를 상속.
"Int에는 멤버 함수가 없습니다." 글쎄, 그들은 +와-와 같은 많은 연산자를 가지고 있습니다.
그래도 멤버 함수가 아닙니다.
OP가 정말로 왜 C ++이 그런지 이해하고 싶다면 Stroustup의 저서 "The Design and Evolution of C ++"를 가져와야합니다. C ++ 초창기의 많은 디자인 결정에 대한 근거를 설명합니다.
int는 클래스가 아닌 네이티브 유형이기 때문에
편집 : 내 댓글을 내 답변으로 이동합니다.
그것은 C 유산과 정확히 원시가 나타내는 것에서 비롯됩니다. C ++의 기본 요소는 컴파일러를 제외하고는 의미가 거의없는 바이트 모음입니다. 반면에 클래스에는 함수 테이블이 있으며 상속 및 가상 상속 경로를 시작하면 vtable이 있습니다. 그것들 중 어느 것도 프리미티브에 존재하지 않으며, 그것을 제시함으로써 a) int가 8 바이트라고 가정하는 많은 c 코드를 깨뜨리고 b) 프로그램이 더 많은 메모리를 차지하게 만듭니다.
다른 방식으로 생각해보십시오. int / float / char에는 데이터 멤버 나 메서드가 없습니다. 프리미티브를 쿼크라고 생각하세요. 세분화 할 수없는 빌딩 블록입니다. 더 큰 것을 만드는 데 사용합니다 (내 비유가 약간 벗어난 경우 사과합니다. 입자 물리학을 충분히 알지 못합니다)
C ++에서 int (및 float 등)의 강력한 타이핑
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
그런 다음 정의 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
.
class MyInt
{
private:
int mInt;
public:
explicit MyInt(int in) { mInt = in; }
// Getters/setters etc
};
그런 다음 유쾌하게 원하는 모든 것을 상속받을 수 있습니다.
아무도 C ++가 (대부분) C와 하위 호환성을 갖도록 설계되어 C 코더의 업그레이드 경로를 쉽게하여 struct
모든 구성원을 public 등으로 기본 설정한다고 언급 한 적이 없습니다 .
int
재정의 할 수있는 기본 클래스를 갖는 것은 그 규칙을 근본적으로 복잡하게 만들고 기존 코더와 컴파일러 공급 업체가 여러분의 신생 언어를 지원하기를 원한다면 아마도 그만한 가치가 없을 것입니다.
C ++에서 내장 유형은 클래스가 아닙니다.
내가 말한 다른 사람들처럼 int는 원시 유형이기 때문에 수행 할 수 없습니다.
하지만 더 강한 타이핑을위한 동기는 이해합니다. 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';
}
그러나 새로운 유형을 정의하고 연산자를 오버로드해야한다는 조언을 받으면 Boost.Operators를 살펴볼 수 있습니다.
글쎄, 당신은 어떤 가상 멤버 함수도 가지지 않은 것을 상속 할 필요가 없습니다. 그래서 경우에도 int
있었다 클래스, 구성을 통해 플러스가 없을 것입니다.
즉, 가상 상속은 어쨌든 상속이 필요한 유일한 실제 이유입니다. 다른 모든 것은 타이핑 시간을 절약하는 것입니다. 그리고 저는 int
가상 멤버가 있는 클래스 / 타입이 C ++ 세계에서 상상할 수있는 가장 현명한 것이라고 생각하지 않습니다 . 적어도 매일은 아닙니다 int
.
int에서 상속한다는 것은 무엇을 의미합니까?
"int"에는 멤버 함수가 없습니다. 멤버 데이터가 없으며 메모리에서 32 (또는 64) 비트 표현입니다. 자체 vtable이 없습니다. 그것이 "갖는"(실제로 그것들을 소유하지도 않는) 모든 것은 멤버 함수보다 정말로 더 많은 전역 함수 인 +-/ *와 같은 일부 연산자입니다.
강력한 typedef로 원하는 것을 얻을 수 있습니다. BOOST_STRONG_TYPEDEF 참조
"INT는 원시적이다"는 사실보다 일반적으로 이것이다 : int
A는 스칼라 타입은 클래스가있는 동안 집계 유형. 스칼라는 원자 값이고 집계는 구성원이있는 것입니다. 상속 (적어도 C ++에 존재하는 경우)은 집계 유형에만 의미가 있습니다. 스칼라에 멤버 나 메서드를 추가 할 수 없기 때문입니다. 정의에 따라 멤버가 없습니다.
이 답변은 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 =!?:%+-*/...(); };
및 제안 된 확장
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.
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 :
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.
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.
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" ;)
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.
I don't know how to explain it any better. How would you inherit from the number 4?
Why can't you inherit from int, even though you might want to?
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):
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.
참고URL : https://stackoverflow.com/questions/2143020/why-cant-i-inherit-from-int-in-c
'Program Tip' 카테고리의 다른 글
자바 스크립트 : 하나의 값을 여러 값과 비교하는 가장 예쁜 방법 (0) | 2020.12.13 |
---|---|
XAML에서 Canvas의 Children 속성을 바인딩 할 수 있습니까? (0) | 2020.12.13 |
루비에서 사용자의 홈 디렉토리를 얻는 크로스 플랫폼 수단? (0) | 2020.12.13 |
하나의 문에 이름이있는 숫자 형 벡터를 만드시겠습니까? (0) | 2020.12.13 |
SQL Server Management Studio에서 저장 프로 시저 코드를 보는 방법 (0) | 2020.12.13 |