从设计的Angular 来看,为什么在C++中没有母类,而在其他语言中通常是object?
object
a8jjtwal1#
C最初被称为“带类的C”。它是C语言的一个发展,不像其他更现代的东西,如C#。你不能把C看作一种语言,而是作为语言的基础(是的,我记得Scott Meyers的书Effective C++)。C本身是一种混合语言,C编程语言及其预处理器。C++添加了另一种混合:
我个人不喜欢一些直接从C到C的东西。一个例子是枚举功能。C#允许开发人员使用它的方式要好得多:它将枚举限制在自己的范围内,它有一个Count属性,并且很容易迭代。由于C希望与C回溯兼容,设计者非常宽容地允许C语言完全进入C++(有一些微妙的差异,但我不记得使用C编译器可以做的任何事情,你不能使用C++编译器)。
zmeyuzjn2#
C++是一种强类型语言,但令人困惑的是,它在模板专门化的上下文中没有一个通用的对象类型。例如,
template <class T> class Hook;template <class ReturnType, class ... ArgTypes>class Hook<ReturnType (ArgTypes...)>{ ... ReturnType operator () (ArgTypes... args) { ... }};
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) { ... }};
template <auto fallback> class Hook;
template <auto fallback, class ReturnType, class ... ArgTypes>
class Hook<ReturnType fallback(ArgTypes...)>
型使用专门的示例化
Hook<some_function> ...
型但是遗憾的是,即使类T可以在专门化之前代替任何类型(类或非类),也没有等价的auto fallback(在本文中,我使用该语法作为最明显的泛型非类型语法)可以在专门化之前代替任何非类型模板参数。因此,一般来说,这种模式不会将类型模板参数转换为非类型模板参数。就像 C++ 中的许多角落一样,答案可能是“没有委员会成员想到它”。
auto fallback
klh5stk13#
在Stroustrup的FAQ中可以找到明确的规则。简而言之,它不传达任何语义。它会有成本。模板对容器更有用。
csbfibhn4#
让我们首先考虑一下为什么你想要一个基类。我可以想到几个不同的原因:1.以支持将对任何类型的对象起作用的泛型操作或集合。1.包括对所有对象通用的各种过程(如内存管理).1.所有东西都是对象(没有原语!)。有些语言(如JavaScript-C)没有这个,这使得事情变得非常混乱。这是Smalltalk、Ruby和Java-C品牌的语言具有基类的两个很好的原因(从技术上讲,Java-C并没有真正的基类,但出于所有意图和目的,它确实有)。对于#1,通过在C++中包含模板,消除了对在单个接口下统一所有对象的基类的需求。例如:
void somethingGeneric(Base);Derived object;somethingGeneric(object);
void somethingGeneric(Base);
Derived object;
somethingGeneric(object);
字符串当你可以通过参数多态性来保持类型完整性时,这是不必要的!
template <class T>void somethingGeneric(T);Derived object;somethingGeneric(object);
template <class T>
void somethingGeneric(T);
型对于#2,而在C2C中,内存管理过程是类实现的一部分,并且从基类继承,C++中的内存管理是使用组合而不是继承来执行的。例如,您可以定义一个智能指针 Package 器,它将对任何类型的对象执行引用计数:
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;};
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;
型这样,你就不用调用对象本身的方法,而是调用它的 Package 器中的方法。这不仅允许更通用的编程:它还允许你分离关注点(因为理想情况下,你的对象应该更关心它应该做什么,而不是在不同的情况下如何管理它的内存)。最后,在像C这样既有原语又有实际对象的语言中,(对于 * 每个**值的一致接口)丢失了,因为这样你就有了某些不符合该接口的值。为了在这种情况下使用原语,你需要把它们提升到对象中(如果你的编译器不能自动完成的话)。这会带来很多复杂性。所以,对你的问题的简短回答是:C没有基类,因为通过模板具有参数多态性,它不需要。
f87krz0w5#
C变量的主要范例是按值传递,而不是按引用传递。强制所有内容都从根Object派生将使按值传递它们本身成为错误。(因为接受一个对象的值作为参数,根据定义会切片它并删除它的灵魂)。这是不受欢迎的。C让你考虑你是想要值语义还是引用语义,给你选择。这是性能计算中的一件大事。
Object
b0zn9rqh6#
**问题是C++中有这样的类型!**它是void。:-)任何指针都可以安全地隐式转换为void *,包括指向基本类型的指针,没有虚表的类和有虚表的类。
void
void *
由于void应该与所有这些类别的对象兼容,因此它本身不能包含虚方法。如果没有虚函数和RTTI,就无法从void获得有关类型的有用信息(它匹配每一种类型,所以只能告诉对每一种类型都正确的事情),但虚函数和RTTI会使简单类型变得非常无效,并阻止C++成为适合直接内存访问等低级编程的语言。所以,有这样的类型。由于语言的低级性质,它只是提供非常简约的(实际上是空的)接口。:-)
6条答案
按热度按时间a8jjtwal1#
C最初被称为“带类的C”。它是C语言的一个发展,不像其他更现代的东西,如C#。你不能把C看作一种语言,而是作为语言的基础(是的,我记得Scott Meyers的书Effective C++)。
C本身是一种混合语言,C编程语言及其预处理器。
C++添加了另一种混合:
我个人不喜欢一些直接从C到C的东西。一个例子是枚举功能。C#允许开发人员使用它的方式要好得多:它将枚举限制在自己的范围内,它有一个Count属性,并且很容易迭代。
由于C希望与C回溯兼容,设计者非常宽容地允许C语言完全进入C++(有一些微妙的差异,但我不记得使用C编译器可以做的任何事情,你不能使用C++编译器)。
zmeyuzjn2#
C++是一种强类型语言,但令人困惑的是,它在模板专门化的上下文中没有一个通用的对象类型。
例如,
字符串
它可以被示例化为
型
现在让我们假设我们想要一个特定的函数。像
型
使用专门的示例化
型
但是遗憾的是,即使类T可以在专门化之前代替任何类型(类或非类),也没有等价的
auto fallback
(在本文中,我使用该语法作为最明显的泛型非类型语法)可以在专门化之前代替任何非类型模板参数。因此,一般来说,这种模式不会将类型模板参数转换为非类型模板参数。
就像 C++ 中的许多角落一样,答案可能是“没有委员会成员想到它”。
klh5stk13#
在Stroustrup的FAQ中可以找到明确的规则。简而言之,它不传达任何语义。它会有成本。模板对容器更有用。
为什么C++没有通用类Object?
csbfibhn4#
让我们首先考虑一下为什么你想要一个基类。我可以想到几个不同的原因:
1.以支持将对任何类型的对象起作用的泛型操作或集合。
1.包括对所有对象通用的各种过程(如内存管理).
1.所有东西都是对象(没有原语!)。有些语言(如JavaScript-C)没有这个,这使得事情变得非常混乱。
这是Smalltalk、Ruby和Java-C品牌的语言具有基类的两个很好的原因(从技术上讲,Java-C并没有真正的基类,但出于所有意图和目的,它确实有)。
对于#1,通过在C++中包含模板,消除了对在单个接口下统一所有对象的基类的需求。例如:
字符串
当你可以通过参数多态性来保持类型完整性时,这是不必要的!
型
对于#2,而在C2C中,内存管理过程是类实现的一部分,并且从基类继承,C++中的内存管理是使用组合而不是继承来执行的。例如,您可以定义一个智能指针 Package 器,它将对任何类型的对象执行引用计数:
型
这样,你就不用调用对象本身的方法,而是调用它的 Package 器中的方法。这不仅允许更通用的编程:它还允许你分离关注点(因为理想情况下,你的对象应该更关心它应该做什么,而不是在不同的情况下如何管理它的内存)。
最后,在像C这样既有原语又有实际对象的语言中,(对于 * 每个**值的一致接口)丢失了,因为这样你就有了某些不符合该接口的值。为了在这种情况下使用原语,你需要把它们提升到对象中(如果你的编译器不能自动完成的话)。这会带来很多复杂性。
所以,对你的问题的简短回答是:C没有基类,因为通过模板具有参数多态性,它不需要。
f87krz0w5#
C变量的主要范例是按值传递,而不是按引用传递。强制所有内容都从根
Object
派生将使按值传递它们本身成为错误。(因为接受一个对象的值作为参数,根据定义会切片它并删除它的灵魂)。
这是不受欢迎的。C让你考虑你是想要值语义还是引用语义,给你选择。这是性能计算中的一件大事。
b0zn9rqh6#
**问题是C++中有这样的类型!**它是
void
。:-)任何指针都可以安全地隐式转换为void *
,包括指向基本类型的指针,没有虚表的类和有虚表的类。由于
void
应该与所有这些类别的对象兼容,因此它本身不能包含虚方法。如果没有虚函数和RTTI,就无法从void
获得有关类型的有用信息(它匹配每一种类型,所以只能告诉对每一种类型都正确的事情),但虚函数和RTTI会使简单类型变得非常无效,并阻止C++成为适合直接内存访问等低级编程的语言。所以,有这样的类型。由于语言的低级性质,它只是提供非常简约的(实际上是空的)接口。:-)