C ++에 기본 클래스가없는 이유는 무엇입니까?


84

빠른 질문 : 디자인 관점에서 볼 때, 왜 C ++에는 마더 오브 올베이스 클래스가 없는데, 보통 object다른 언어로는 무엇입니까?


27
이것은 디자인 질문 이라기보다는 철학적 인 질문에 가깝습니다. 짧은 대답은 "Bjarne이 분명히 그것을 원하지 않았기 때문입니다."입니다.
Jerry Coffin 2011

4
또한 당신이 하나를 만드는 것을 막을 수는 없습니다.
GWW

22
개인적으로 모든 것이 동일한 기본 클래스에서 파생 된 것은 설계상의 결함이라고 생각합니다. 그것은 가치가있는 것보다 더 많은 문제를 일으키는 프로그래밍 스타일을 장려합니다. 자동차와 명령 자동화 개체를 모두 수용 할 수있는 컨테이너를 정말로 원합니까?
Martin York

3
@Martin하지만 이런 식으로보세요 typedef. 보다 일반적인 클래스 계층 구조에서는 object또는을 사용할 수 있습니다 iterator.
slezica 2011

9
@Santiago : 통합 유형 시스템을 사용한다는 것은 거의 항상 다형성, 동적 디스패치 및 RTTI에 의존하는 많은 코드로 끝나는 것을 의미하며, 모두 상대적으로 비용이 많이 들고 최적화가 불가능할 때 모두 가능합니다. 사용되지 않습니다.
James McNellis 2011

답변:


116

최종 판결은 Stroustrup의 FAQ 에서 찾을 수 있습니다. 요컨대 의미 론적 의미를 전달하지 않습니다. 비용이 발생합니다. 템플릿은 컨테이너에 더 유용합니다.

C ++에 유니버설 클래스 Object가없는 이유는 무엇입니까?

  • 우리는 하나가 필요하지 않습니다. 제네릭 프로그래밍은 대부분의 경우 정적으로 형식이 안전한 대안을 제공합니다. 다른 경우는 다중 상속을 사용하여 처리됩니다.

  • 유용한 유니버설 클래스는 없습니다. 진정한 유니버설은 그 자체의 의미를 가지고 있지 않습니다.

  • "범용"클래스는 유형과 인터페이스에 대한 엉성한 사고를 장려하고 과도한 런타임 검사로 이어집니다.

  • 범용 기본 클래스를 사용하는 것은 비용을 의미합니다. 객체는 다형성이되도록 힙 할당되어야합니다. 이는 메모리 및 액세스 비용을 의미합니다. 힙 개체는 자연스럽게 복사 의미 체계를 지원하지 않습니다. 힙 개체는 리소스 관리를 복잡하게하는 단순 범위 동작을 지원하지 않습니다. 범용 기본 클래스는 dynamic_cast 및 기타 런타임 검사 사용을 권장합니다.


83
우리는 필요하지 않습니다 / 기본 클래스 개체 / 우리가 더 필요하지 않습니다 / ... 컨트롤을 생각한다)
Piskvor 건물 남아 있지

3
@Piskvor-LOL 뮤직 비디오보고 싶어요!
Byron Whitlock 2011

10
Objects must be heap-allocated to be polymorphic-이 진술은 일반적으로 옳지 않다고 생각합니다. 스택에 다형성 클래스의 인스턴스를 확실히 만들고 기본 클래스 중 하나에 대한 포인터로 전달하여 다형성 동작을 생성 할 수 있습니다.
Byron

5
자바에서 참조 대를 캐스팅하려고 Foo참조 대에 BarBar하지 상속 않는 Foo, 결정 론적 예외가 발생합니다. C ++에서 포인터 -Foo를 포인터-바로 캐스트하려고 시도하면 로봇을 제 시간에 돌려서 Sarah Connor를 죽일 수 있습니다.
supercat

3
@supercat 이것이 우리가 dynamic_cast <>를 사용하는 이유입니다. SkyNet과 신비하게 나타나는 Chesterfield 소파를 피하기 위해.
캡틴 기린

44

우선 왜베이스 클래스를 원하는지 생각해 봅시다. 몇 가지 다른 이유를 생각할 수 있습니다.

  1. 모든 유형의 개체에서 작동하는 일반 작업 또는 컬렉션을 지원합니다.
  2. 모든 개체에 공통적 인 다양한 절차를 포함합니다 (예 : 메모리 관리).
  3. 모든 것이 객체입니다 (프리미티브가 아닙니다!). Objective-C와 같은 일부 언어에는이 기능이 없기 때문에 상황이 매우 복잡해집니다.

이것이 Smalltalk, Ruby 및 Objective-C 브랜드의 언어가 기본 클래스를 갖는 두 가지 좋은 이유입니다 (기술적으로 Objective-C에는 실제로 기본 클래스가 없지만 모든 의도와 목적에 따라 그렇습니다).

# 1의 경우, C ++에 템플릿을 포함하여 모든 개체를 단일 인터페이스로 통합하는 기본 클래스가 필요하지 않습니다. 예를 들면 :

void somethingGeneric(Base);

Derived object;
somethingGeneric(object);

파라 메트릭 다형성을 통해 유형 무결성을 완전히 유지할 수 있다면 불필요합니다!

template <class T>
void somethingGeneric(T);

Derived object;
somethingGeneric(object);

# 2의 경우 Objective-C에서는 메모리 관리 절차가 클래스 구현의 일부이고 기본 클래스에서 상속되는 반면 C ++의 메모리 관리는 상속이 아닌 구성을 사용하여 수행됩니다. 예를 들어 모든 유형의 객체에 대한 참조 계산을 수행하는 스마트 포인터 래퍼를 정의 할 수 있습니다.

template <class T>
struct refcounted
{
  refcounted(T* object) : _object(object), _count(0) {}

  T* operator->() { return _object; }
  operator T*() { return _object; }

  void retain() { ++_count; }

  void release()
  {
    if (--_count == 0) { delete _object; }
  }

  private:
    T* _object;
    int _count;
};

그런 다음 개체 자체에서 메서드를 호출하는 대신 래퍼에서 메서드를 호출합니다. 이것은보다 일반적인 프로그래밍을 허용 할뿐만 아니라 관심사를 분리 할 수도 있습니다 (이상적으로는 객체가 다른 상황에서 메모리를 관리하는 방법보다 수행해야하는 작업에 더 관심을 가져야하기 때문에).

마지막으로, C ++와 같은 기본 객체와 실제 객체를 모두 포함하는 언어에서는 기본 클래스 ( 모든 값에 대한 일관된 인터페이스)를 갖는 이점 이 손실됩니다. 그러면 해당 인터페이스를 준수 할 수없는 특정 값이 있기 때문입니다. 이런 상황에서 프리미티브를 사용하기 위해서는 그것들을 객체로 들어 올려야합니다 (컴파일러가 자동으로하지 않는다면). 이것은 많은 합병증을 만듭니다.

따라서 귀하의 질문에 대한 짧은 대답은 C ++에는 기본 클래스가 없습니다. 왜냐하면 템플릿을 통한 매개 변수 다형성을 가지므로 그럴 필요가 없기 때문입니다.


괜찮아! 당신이 도움이 :) 발견 다행
조나단 스털링

2
포인트 3의 경우 C #으로 대응합니다. C #에는 ( ) 로 boxing 할 수 있는 프리미티브가 있지만 반드시 그럴 필요는 없습니다. 컴파일러, 그리고 별칭이 있고 상호 교환 사용할 수 있습니다; 런타임은 필요할 때 권투를 처리합니다. objectSystem.ObjectintSystem.Int32
Cole Johnson

C ++에서는 권투가 필요 없습니다. 템플릿을 사용하면 컴파일러는 컴파일 시간에 올바른 코드를 생성합니다.
Rob K

2
이 (이전) 답변은 참조 계산 스마트 포인터를 보여 주지만 이제 C ++ 11에는 std::shared_ptr대신 사용해야합니다.

15

C ++ 변수의 지배적 인 패러다임은 참조에 의한 전달이 아니라 값에 의한 전달입니다. 모든 것이 루트에서 파생되도록 강제 Object하면 값으로 전달하는 것은 사실 오류가됩니다.

(값으로 Object를 매개 변수로 받아들이 기 때문에 정의에 따라 슬라이스하고 영혼을 제거합니다).

이것은 반갑지 않습니다. C ++를 사용하면 값 또는 참조 의미론을 원하는지 여부를 생각하여 선택할 수 있습니다. 이것은 성능 컴퓨팅에서 중요한 부분입니다.


1
그 자체로는 오류가 아닙니다. 유형 계층은 이미 존재하며 적절한 경우 값별 전달을 매우 잘 활용합니다. 그러나 참조에 의한 전달이 더 적절한 경우 더 많은 힙 기반 전략을 권장합니다.
Dennis Zickefoose 2011

IMHO, 좋은 언어는 파생 클래스가 참조 별 조각을 사용하여 기본 클래스 개체로 합법적으로 사용될 수있는 상황, 값별 조각을 사용하여 하나로 변환 될 수있는 상황에 대해 고유 한 종류의 상속을 가져야합니다. 그리고 둘 다 합법적이지 않은 사람들. 슬라이스 할 수있는 항목에 대해 공통 기본 유형을 갖는 값은 제한되지만 많은 항목은 슬라이스 할 수 없으며 간접적으로 저장되어야합니다. 그러한 것들을 공통점에서 파생시키는 데 드는 추가 비용은 Object상대적으로 적을 것입니다.
supercat

6

문제는 C ++에 이러한 유형이 있다는 것입니다! 입니다 void. :-) void *기본 유형에 대한 포인터, 가상 테이블이없는 클래스 및 가상 테이블이있는 클래스를 포함하여 모든 포인터를으로 안전하게 캐스트 할 수 있습니다 .

모든 범주의 객체와 호환되어야하므로 void가상 메서드를 포함 할 수 없습니다. 가상 함수와 RTTI가 없으면 유형에 대한 유용한 정보를 얻을 수 없지만 void(모든 유형과 일치하므로 모든 유형에 맞는 것만 알 수 있음), 가상 함수와 RTTI는 단순한 유형을 매우 비효율적으로 만들고 C ++가 존재하지 않도록합니다. 직접 메모리 액세스 등의 저수준 프로그래밍에 적합한 언어

그래서 그런 유형이 있습니다. 언어의 저수준 특성으로 인해 매우 최소한의 (사실 빈) 인터페이스를 제공합니다. :-)


포인터 유형과 객체 유형은 동일하지 않습니다. 유형의 개체를 가질 수 없습니다 void.
Kevin Panko 2013-08-02

1
실제로 이것이 C ++의 상속이 일반적으로 작동하는 방식입니다. 가장 중요한 작업 중 하나는 포인터와 참조 형식의 호환성입니다. 클래스에 대한 포인터가 필요한 경우 파생 클래스에 대한 포인터가 제공 될 수 있습니다. 그리고 두 유형간에 포인터를 static_cast하는 기능은 계층 구조로 연결된 신호입니다. 이 관점에서 void는 확실히 C ++의 범용 기본 클래스입니다.
Ellioh 2013-08-05

유형의 변수를 선언하면 void컴파일되지 않고이 오류가 발생 합니다. Java에서는 유형의 변수를 가질 수 있으며 Object작동합니다. 이것이 void"실제"유형과 의 차이점 입니다. void모든 것에 대한 기본 유형 이 사실 일 수 있지만 생성자, 메서드 또는 필드를 제공하지 않으므로 존재 여부를 알 수있는 방법이 없습니다. 이 주장은 증명되거나 반증 될 수 없습니다.
Kevin Panko

1
Java에서 모든 변수는 참조입니다. 그 차이입니다. :-) (BTW, C ++에는 변수를 선언하는 데 사용할 수없는 추상 클래스도 있습니다). 그리고 내 대답에서 RTTI를 무효화하는 것이 불가능한 이유를 설명했습니다. 따라서 이론적으로 void는 여전히 모든 것에 대한 기본 클래스입니다. static_cast는 관련 유형간에 만 캐스트를 수행하기 때문에 증명이며 void에 사용할 수 있습니다. 그러나 당신이 옳습니다. RTTI 액세스가없는 경우에는 상위 수준 언어의 기본 유형과 매우 다릅니다.
Ellioh

1
@Ellioh C ++ 11 표준 §3.9.1p9를 참조한 후, 그것이 void유형 이라는 것을 인정합니다 (그리고 약간 영리한 사용을? : 발견했습니다 ). 하나는 "완료 할 수없는 불완전한 유형"이고 다른 하나는 void&유형 이 없습니다 .
bcrist 2015 년

-2

C ++는 강력한 형식의 언어입니다. 그러나 템플릿 전문화의 맥락에서 범용 객체 유형이 없다는 것은 당혹 스럽습니다.

예를 들어, 패턴

template <class T> class Hook;
template <class ReturnType, class ... ArgTypes>
class Hook<ReturnType (ArgTypes...)>
{
   ...
   ReturnType operator () (ArgTypes... args) { ... }
};

다음과 같이 인스턴스화 될 수 있습니다.

Hook<decltype(some_function)> ...;

이제 특정 함수에 대해 동일한 것을 원한다고 가정 해 봅시다. 처럼

template <auto fallback> class Hook;
template <auto fallback, class ReturnType, class ... ArgTypes>
class Hook<ReturnType fallback(ArgTypes...)>
{
   ...
   ReturnType operator () (ArgTypes... args) { ... }
};

전문화 된 인스턴스화

Hook<some_function> ...

그러나 아아, 클래스 T가 전문화 전에 어떤 유형 (클래스이든 auto fallback아니든)을 나타낼 수 있지만, 어떤 것을 의미 할 수있는 동등한 것은 없습니다 (이 컨텍스트에서 가장 명백한 일반 비 유형 유형으로 해당 구문을 사용하고 있습니다). 전문화 전 비 유형 템플릿 인수.

따라서 일반적으로이 패턴은 형식 템플릿 인수에서 형식이 아닌 템플릿 인수로 전송되지 않습니다.

C ++ 언어의 많은 구석과 마찬가지로 대답은 "위원회 구성원이 생각하지 않은 것"일 것입니다.


(같은 것) 당신 ReturnType fallback(ArgTypes...)이 일할 지라도 그것은 나쁜 디자인 일 것입니다. template <class T, auto fallback> class Hook; template <class ReturnType, class ... ArgTypes> class Hook<ReturnType (ArgTypes...), ReturnType(*fallback)(ArgTypes...)> ...원하는 것을 수행하고 템플릿 매개 변수 Hook가 일관된 종류
Caleth

-4

C ++는 처음에 "C with classes"라고 불 렸습니다. C #과 같은 다른 현대적인 것과는 달리 C 언어의 발전입니다. 그리고 C ++를 언어로 볼 수는 없지만 언어의 기초로 볼 수 있습니다 (예, Scott Meyers의 저서 Effective C ++를 기억하고 있습니다).

C 자체는 언어, C 프로그래밍 언어 및 전 처리기의 혼합입니다.

C ++는 또 다른 조합을 추가합니다.

  • 클래스 / 객체 접근 방식

  • 템플릿

  • STL

저는 개인적으로 C에서 C ++로 직접 오는 것을 좋아하지 않습니다. 한 가지 예는 열거 형 기능입니다. C #을 통해 개발자가 사용할 수있는 방법이 훨씬 더 좋습니다. 자체 범위에서 열거 형을 제한하고 Count 속성이 있으며 쉽게 반복 할 수 있습니다.

C ++가 C와 역 호환되기를 원했기 때문에 디자이너는 C 언어가 전체적으로 C ++에 들어가도록 허용하는 것이 매우 관대했습니다 (미묘한 차이가 있지만 C 컴파일러를 사용하여 수행 할 수있는 작업은 C ++ 컴파일러를 사용할 수 없습니다).


"C ++를 언어로 볼 수는 없지만 언어의 기초로 볼 수 있습니다."... 그 기괴한 진술이 무엇을 주도하고 있는지 실제로 설명하고 설명하는 데 관심이 있습니까? 다중 패러다임은 "다중 언어"와 구별되지만 전처리 기가 나머지 컴파일러 단계와 분리되어 있다고 말하는 것이 타당합니다. 좋은 지적입니다. Re enums-원하는 경우 클래스 범위에 사소하게 래핑 할 수 있으며 전체 언어에는 내부 검사가 부족합니다.이 경우 근사치가 될 수 있지만 주요 문제입니다. Boost vault의 benum 코드를 참조하십시오. 당신의 요점은 Q에 불과 합니까? C ++는 보편적 기반에서 시작 되지 않았 습니까?
Tony Delroy 2011

@Tony : 흥미롭게도, 그들이 다른 언어로 언급하지도 않은 유일한 책은 전 처리기입니다. 전처리 기가 자신의 입력을 먼저 구문 분석하므로 귀하의 관점을 이해합니다. 그러나 템플릿 메커니즘을 갖는 것은 일반화를 수행하는 다른 언어를 갖는 것과 같습니다. 템플릿 구문을 적용하면 모든 단일 유형에 대한 함수를 생성하는 컴파일러가 있습니다. C로 작성된 프로그램을 C ++ 컴파일러에 입력 할 수있는 경우 이것은 하나의 두 언어입니다. C와 C with objects => C ++
sergiol

STL은 추가 기능으로 볼 수 있지만 템플릿을 통해 기본적으로 C ++의 기능을 확장하는 매우 실용적인 클래스와 컨테이너가 있습니다. 그리고 내가 말한 열거 형은 NATIVE 열거 형입니다. Boost에 대해 말할 때 언어에 고유하지 않은 타사 코드에 의존하고 있습니다. C #에서는 반복 가능하고 자체 범위 열거 형을 사용하기 위해 extern을 포함 할 필요가 없습니다. C ++에서 자주 사용하는 트릭은 열거 형의 마지막 항목을 할당 된 값없이 호출하는 것입니다. 따라서 자동으로 0부터 시작되고 증가합니다. 즉, NUM_ITEMS와 같이 상한을 제공합니다.
sergiol 2011

2
추가 설명을 해주셔서 감사합니다-적어도 당신이 어디에서 왔는지 볼 수 있습니다. 나는 몇몇 사람들이 템플릿 사용법을 배우는 것이 제 경험은 아니지만 다른 C ++ 프로그래밍과 분리되어 있다고 불평하는 것을 들었습니다. 나는 다른 독자들에게 당신이 제시하는 다른 관점에서 그들이 원하는 것을 만들도록 남겨 둘 것입니다. 건배.
Tony Delroy 2011

1
범용 기본 유형을 갖는 가장 큰 문제점은 가상 메소드가 없으면 그러한 유형이 쓸모가 없다는 것입니다. C ++는 가상 메서드가없는 구조체 형식에 대한 오버 헤드를 추가하지 않았지만 가상 메서드가있는 모든 형식의 모든 개체에는 최소한 디스패치 테이블에 대한 포인터가 있어야합니다. 클래스와 구조가 서로 다른 유니버스에 있었다면 유니버설 클래스 개체를 가질 수 있었을 수도 있지만 클래스와 구조체는 기본적으로 C ++에서 동일합니다.
supercat 2013-08-06
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.