在 C++ 中本质上不是只有一个 "kind" 的多态性吗?
Isn't there essentially only one "kind" of polymorphism in C++?
也就是:函数重载。
我的意思是
base_ptr->derived_class_method()
本质上是
derived_class_method(base* this_ptr)
这是通过函数重载实现的多态性。
通过模板实现多态性也是如此,包括CRTP等
因此,考虑到最广泛的实现,多态性的所有 "various kinds" 似乎都归结为 函数重载 。
还是我遗漏了什么?
您遗漏了一些东西,但主要是一个关键区别:重载是编译时,而覆盖(即 virtual
)可以是运行时。通过重载,您可以这样做:
void make_a_sound(Cat const& cat)
{
std::cout << cat << ": meow" << std::endl;
}
void make_a_sound(Dog const& dog)
{
std::cout << dog << ": wurf" << std::endl;
}
int main()
{
Cat c;
Dog d;
make_a_sound(c);
make_a_sound(d);
}
但是你不能这样做:
class Animal
{
virtual void make_a_sound() = 0;
};
void make_a_sound(std::vector<Animal*> const& animals)
{
for (Animal* a : animals)
a->make_a_sound();
}
在第一个示例中,编译器将 select 在 编译时 调用正确的函数。你可以用不同的方式命名这两个函数(即 make_a_sound_cat
和 make_a_sound_dog
),它的工作原理是一样的,能够重复使用相同的名称只是为了方便。
然而,在第二个示例中,编译器生成代码,以便在 运行时,您的程序将找出每只动物的确切性质并调用正确的方法。
基本上三种多态性:
1。临时
2.参数
3. 子类型
1。特设:
Ad-hoc 多态性允许具有相同名称的函数对每种类型执行不同的操作。 (这是你举的例子)
示例:
int add(int a, int b) {
return a + b;
}
std::string add(const char *a, const char *b) {
std::string result(a);
result += b;
return result;
}
2。参数化:
参数多态性提供了一种为任何类型执行相同代码的方法。它是通过 C++ 中的模板实现的。 (所以根据定义,你可以说它也是一种自动函数重载)
3。子类型:
子类型化是通过使用派生的 classes 通过基 class 指针和引用来实现的。所以在实践中我们使用 subclasses 和 virtual 方法来进行子类型化。 (这不是函数重载)
class Mammal
{
public:
Mammal() {};
virtual ~Mammal() {};
virtual std::string SendLoudNoise()
{
std::string str("I am a generic mammal");
return str;
}
};
class Dog : public Mammal {
public:
Dog() {};
virtual ~Dog() {};
std::string SendLoudNoise()
{
std::string str("Woof woof!");
return str;
}
};
class Cat : public Mammal {
public:
Cat() {};
virtual ~Cat() {};
std::string SendLoudNoise()
{
std::string str("--- Twitch my tail ---");
return str;
}
};
int main()
{
Mammal* pAnimal[2] = { new Dog, new Cat};
for (int i = 0 ; i < 2; ++i)
{
std::cout << pAnimal[i]->SendLoudNoise() << std::endl;
delete pAnimal[i];
}
return 0;
}
您无法通过函数重载实现子类型化。因为它是'运行时多态性'。
Isn't there essentially only one “kind”
取决于你如何定义"kind"。 Wikipedia 将多态性分为 3 类:Ad hoc、parametric 和 subtyping。所有这些类型的多态性都存在于 C++ 中。
And that is: function overloading
在 C++ 语言的上下文中,function overloading 一词具有特定的含义。就是这个意思,既不能调用模板,也不能调用虚函数函数重载.
然而,抽象地说,是的,所有多态性(在 C++ 中,也许在一般的计算机科学中)归结为选择一个函数实现而不是另一个。
C++中有3个。
主要区别在于 "polymorphs" 的独立程度。
- 重载是临时多态性。
它是临时的,因为您只是使用相同的名称来表示几个不同的功能,它们不需要以任何方式相关。
示例:
int foo(int);
void foo(const std::string&, double);
- 函数模板提供参数多态性。
这些只依赖于其参数的接口("interface"是广义的;不是Java的意义)。
示例:
template<typename T>
T id(T t)
{
return t;
}
这为每个 T
定义了一个函数,如果 T
是可复制构造的。
要问的一个有效问题是 "isn't this a kind of automatic overloading?",答案是 C++ 中的差异并不大。
在其他语言中,id
可能只是一个函数,可用于任何类型。
- "Virtual functions" - 覆盖 - 是子类型。
这些与特定的类型层次结构紧密相关。
您会注意到重载要求多态具有不同的原型,而其他两个要求原型相同。
此外,虚函数(通常)在运行时绑定,而其他两个总是在编译时绑定(在 C++ 中)。
这里是 C++ 中所有可能的多态性的完整列表:
- 临时多态性
- 强制多态(隐式转换)
- 重载多态,可以是
- 函数重载
- 运算符重载
- 通用多态性
- 包含多态性(子类型化)
- 参数或通用多态性
也就是:函数重载。
我的意思是
base_ptr->derived_class_method()
本质上是
derived_class_method(base* this_ptr)
这是通过函数重载实现的多态性。
通过模板实现多态性也是如此,包括CRTP等
因此,考虑到最广泛的实现,多态性的所有 "various kinds" 似乎都归结为 函数重载 。
还是我遗漏了什么?
您遗漏了一些东西,但主要是一个关键区别:重载是编译时,而覆盖(即 virtual
)可以是运行时。通过重载,您可以这样做:
void make_a_sound(Cat const& cat)
{
std::cout << cat << ": meow" << std::endl;
}
void make_a_sound(Dog const& dog)
{
std::cout << dog << ": wurf" << std::endl;
}
int main()
{
Cat c;
Dog d;
make_a_sound(c);
make_a_sound(d);
}
但是你不能这样做:
class Animal
{
virtual void make_a_sound() = 0;
};
void make_a_sound(std::vector<Animal*> const& animals)
{
for (Animal* a : animals)
a->make_a_sound();
}
在第一个示例中,编译器将 select 在 编译时 调用正确的函数。你可以用不同的方式命名这两个函数(即 make_a_sound_cat
和 make_a_sound_dog
),它的工作原理是一样的,能够重复使用相同的名称只是为了方便。
然而,在第二个示例中,编译器生成代码,以便在 运行时,您的程序将找出每只动物的确切性质并调用正确的方法。
基本上三种多态性:
1。临时
2.参数
3. 子类型
1。特设:
Ad-hoc 多态性允许具有相同名称的函数对每种类型执行不同的操作。 (这是你举的例子)
示例:
int add(int a, int b) {
return a + b;
}
std::string add(const char *a, const char *b) {
std::string result(a);
result += b;
return result;
}
2。参数化:
参数多态性提供了一种为任何类型执行相同代码的方法。它是通过 C++ 中的模板实现的。 (所以根据定义,你可以说它也是一种自动函数重载)
3。子类型:
子类型化是通过使用派生的 classes 通过基 class 指针和引用来实现的。所以在实践中我们使用 subclasses 和 virtual 方法来进行子类型化。 (这不是函数重载)
class Mammal
{
public:
Mammal() {};
virtual ~Mammal() {};
virtual std::string SendLoudNoise()
{
std::string str("I am a generic mammal");
return str;
}
};
class Dog : public Mammal {
public:
Dog() {};
virtual ~Dog() {};
std::string SendLoudNoise()
{
std::string str("Woof woof!");
return str;
}
};
class Cat : public Mammal {
public:
Cat() {};
virtual ~Cat() {};
std::string SendLoudNoise()
{
std::string str("--- Twitch my tail ---");
return str;
}
};
int main()
{
Mammal* pAnimal[2] = { new Dog, new Cat};
for (int i = 0 ; i < 2; ++i)
{
std::cout << pAnimal[i]->SendLoudNoise() << std::endl;
delete pAnimal[i];
}
return 0;
}
您无法通过函数重载实现子类型化。因为它是'运行时多态性'。
Isn't there essentially only one “kind”
取决于你如何定义"kind"。 Wikipedia 将多态性分为 3 类:Ad hoc、parametric 和 subtyping。所有这些类型的多态性都存在于 C++ 中。
And that is: function overloading
在 C++ 语言的上下文中,function overloading 一词具有特定的含义。就是这个意思,既不能调用模板,也不能调用虚函数函数重载.
然而,抽象地说,是的,所有多态性(在 C++ 中,也许在一般的计算机科学中)归结为选择一个函数实现而不是另一个。
C++中有3个。
主要区别在于 "polymorphs" 的独立程度。
- 重载是临时多态性。
它是临时的,因为您只是使用相同的名称来表示几个不同的功能,它们不需要以任何方式相关。
示例:
int foo(int);
void foo(const std::string&, double);
- 函数模板提供参数多态性。
这些只依赖于其参数的接口("interface"是广义的;不是Java的意义)。
示例:
template<typename T>
T id(T t)
{
return t;
}
这为每个 T
定义了一个函数,如果 T
是可复制构造的。
要问的一个有效问题是 "isn't this a kind of automatic overloading?",答案是 C++ 中的差异并不大。
在其他语言中,id
可能只是一个函数,可用于任何类型。
- "Virtual functions" - 覆盖 - 是子类型。
这些与特定的类型层次结构紧密相关。
您会注意到重载要求多态具有不同的原型,而其他两个要求原型相同。
此外,虚函数(通常)在运行时绑定,而其他两个总是在编译时绑定(在 C++ 中)。
这里是 C++ 中所有可能的多态性的完整列表:
- 临时多态性
- 强制多态(隐式转换)
- 重载多态,可以是
- 函数重载
- 运算符重载
- 通用多态性
- 包含多态性(子类型化)
- 参数或通用多态性