为什么C++中没有通用基类?

发布于 2024-11-01 15:30:04 字数 59 浏览 3 评论 0原文

从设计的角度来看,为什么在C++中没有母类,而在其他语言中通常是object

From a design point of view, why is that, in C++, there is no mother-of-all base-class, what's usually object in other languages?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

棒棒糖 2024-11-08 15:30:04

最终裁决可在 Stroustrup 常见问题解答中找到。
简而言之,它不传达任何语义意义。这是有代价的。模板对于容器来说更有用。

为什么 C++ 没有通用类 Object?

  • 我们不需要一个:泛型编程在大多数情况下提供静态类型安全的替代方案。其他情况使用多重继承来处理。

  • 不存在有用的通用类:真正的通用类不具有自己的语义。

  • “通用”类会鼓励对类型和接口的草率思考,并导致过多的运行时检查。

  • 使用通用基类意味着成本:对象必须通过堆分配才能实现多态;这意味着内存和访问成本。堆对象自然不支持复制语义。堆对象不支持简单的作用域行为(这使资源管理变得复杂)。通用基类鼓励使用dynamic_cast和其他运行时检查。

The definitive ruling is found in Stroustrup's FAQs.
In short, it doesn't convey any semantic meaning. It will have a cost. Templates are more useful for containers.

Why doesn't C++ have a universal class Object?

  • We don't need one: generic programming provides statically type safe alternatives in most cases. Other cases are handled using multiple inheritance.

  • There is no useful universal class: a truly universal carries no semantics of its own.

  • A "universal" class encourages sloppy thinking about types and interfaces and leads to excess run-time checking.

  • Using a universal base class implies cost: Objects must be heap-allocated to be polymorphic; that implies memory and access cost. Heap objects don't naturally support copy semantics. Heap objects don't support simple scoped behavior (which complicates resource management). A universal base class encourages use of dynamic_cast and other run-time checking.

昇り龍 2024-11-08 15:30:04

让我们首先考虑一下为什么你想要一个基类。我可以想到几个不同的原因:

  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++ 没有基类,因为通过模板具有参数多态性,它不需要。

Let's first think about why you'd want to have a base-class in the first place. I can think of a few different reasons:

  1. To support generic operations or collections that will work on objects of any type.
  2. To include various procedures which are common to all objects (such as memory management).
  3. Everything is an object (no primitives!). Some languages (like Objective-C) don't have this, which makes things pretty messy.

These are the two good reasons that languages of the Smalltalk, Ruby and Objective-C brand have base-classes (technically, Objective-C doesn't really have a base-class, but for all intents and purposes, it does).

For #1, the need for a base-class that unifies all objects under a single interface is obviated by the inclusion of templates in C++. For instance:

void somethingGeneric(Base);

Derived object;
somethingGeneric(object);

is unnecessary, when you can maintain type integrity all the way through by means of parametric polymorphism!

template <class T>
void somethingGeneric(T);

Derived object;
somethingGeneric(object);

For #2, whereas in Objective-C, memory management procedures are part of a class's implementation, and are inherited from the base class, memory management in C++ is performed using composition rather than inheritance. For instance, you can define a smart pointer wrapper which will perform reference counting on objects of any type:

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

Then, instead of calling methods on the object itself, you'd be calling methods in its wrapper. This not only allows more generic programming: it also lets you separate concerns (since ideally, your object should be more concerned about what it should do, than how its memory should be managed in different situations).

Lastly, in a language that has both primitives and actual objects like C++, the benefits of having a base-class (a consistent interface for every value) are lost, since then you have certain values which cannot conform to that interface. In order to use primitives in that sort of a situation, you need to lift them into objects (if your compiler won't do it automatically). This creates a lot of complication.

So, the short answer to your question: C++ doesn't have a base-class because, having parametric polymorphism through templates, it doesn't need to.

丢了幸福的猪 2024-11-08 15:30:04

C++ 变量的主要范例是按值传递,而不是按引用传递。强制所有内容都从根 Object 派生将使按值传递它们本身成为错误。

(因为按值接受对象作为参数,根据定义会对其进行切片并删除其灵魂)。

这是不受欢迎的。 C++ 让您思考是否需要值语义或引用语义,从而为您提供选择。这是性能计算中的一件大事。

The dominant paradigm for C++ variables is pass-by-value, not pass-by-ref. Forcing everything to be derived from a root Object would make passing them by value an error ipse facto.

(Because accepting an Object by value as parameter, would by definition slice it and remove its soul).

This is unwelcome. C++ makes you think about whether you wanted value or reference semantics, giving you the choice. This is a big thing in performance computing.

昔日梦未散 2024-11-08 15:30:04

问题是C++中有这样一种类型!它是void。 :-) 任何指针都可以安全地隐式转换为 void *,包括指向基本类型、没有虚表的类和有虚表的类的指针。

由于它应该与所有这些类别的对象兼容,因此 void 本身不能包含虚拟方法。如果没有虚函数和 RTTI,则无法从 void 获得有关类型的有用信息(它匹配每种类型,因此只能告诉每种类型都正确的内容),但是虚函数和 RTTI 会使事情变得简单类型非常低效,并且阻止 C++ 成为适合直接内存访问等低级编程的语言。

所以,有这样的类型。由于该语言的低级性质,它只是提供了非常简约(实际上是空的)界面。 :-)

The problem is that there IS such a type in C++! It is void. :-) Any pointer may be safely implicitly cast to void *, including pointers to basic types, classes with no virtual table and classes with virtual table.

Since it should be compatible with all those categories of objects, void itself can not contain virtual methods. Without virtual functions and RTTI, no useful information on type can be obtained from void (it matches EVERY type, so can tell only things that are true for EVERY type), but virtual functions and RTTI would make simple types very ineffective and stop C++ from being a language suitable for low-level programming with direct memory access etc.

So, there is such type. It just provides very minimalistic (in fact, empty) interface due to low-level nature of the language. :-)

旧梦荧光笔 2024-11-08 15:30:04

C++ 最初被称为“带有类的 C”。
它是 C 语言的发展,与 C# 等其他一些更现代的语言不同。
而且你不能将 C++ 视为一种语言,而是将其视为语言的基础(是的,我记得 Scott Meyers 的书《Effective C++》)。

C 本身是多种语言、C 编程语言及其预处理器的混合体。

C++ 添加了另一种组合:

  • 类/对象方法

  • 模板

  • STL

我个人不喜欢某些东西直接从 C 到 C++。枚举功能就是一个例子。 C# 允许开发人员使用它的方式要好得多:它将枚举限制在其自己的范围内,它具有 Count 属性并且很容易迭代。

由于C++想要与C向后兼容,设计者非常宽容地允许C语言整体进入C++(有一些细微的差别,但我不记得你可以使用你自己的C编译器做任何事情)使用 C++ 编译器无法做到)。

C++ was initially called "C with classes".
It is a progression of the C language, unlike some other more modern things like C#.
And you can not see C++ as a language, but as a foundation of languages (Yes, I am remembering the Scott Meyers book Effective C++).

C itself is a mix of languages, the C programming language and its preprocessor.

C++ adds another mix:

  • the class/objects approach

  • templates

  • the STL

I personally don't like some stuff that come directly from C to C++. One example is the enum feature. The way C# allows the developer to use it is way better: it limits the enum in its own scope, it has a Count property and it is easily iterable.

As C++ wanted to be retrocompatible with C, the designer was very permissive to allow the C language to enter in its whole to C++ (there are some subtle differences, but I do not remember any thing that you could do using a C compiler that you could not do using a C++ compiler).

自找没趣 2024-11-08 15:30:04

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 可以在专门化之前代表任何类型(类或非类),但没有等效的自动回退(我使用该语法作为最明显的泛型)在这种情况下,非类型一)可以在专门化之前代表任何非类型模板参数。

因此,一般来说,此模式不会从类型模板参数转移到非类型模板参数。

就像 C++ 语言中的许多角落一样,答案可能是“没有委员会成员想到它”。

C++ is a strongly typed language. Yet it is puzzling that it does not have a universal object type in the context of template specialization.

Take, for example, the pattern

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

which can be instantiated as

Hook<decltype(some_function)> ...;

Now let's assume that we want the same for a particular function. Like

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

with the specialized instantiation

Hook<some_function> ...

But alas, even though class T can stand in for any type (class or not) before specialization, there is no equivalent auto fallback (I am using that syntax as the most obvious generic non-type one in this context) that could stand in for any non-type template argument before specialization.

So in general this pattern does not transfer from type template arguments to non-type template arguments.

Like with a lot of corners in the C++ language, the answer is likely "no committee member thought of it".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文