C++ 虚函数可以有默认参数吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3533589/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can virtual functions have default parameters?
提问by Arnold Spence
If I declare a base class (or interface class) and specify a default value for one or more of its parameters, do the derived classes have to specify the same defaults and if not, which defaults will manifest in the derived classes?
如果我声明一个基类(或接口类)并为其一个或多个参数指定一个默认值,派生类是否必须指定相同的默认值,如果没有,哪些默认值将在派生类中体现?
Addendum: I'm also interested in how this may be handled across different compilers and any input on "recommended" practice in this scenario.
附录:我也对如何在不同编译器之间处理这种情况以及在这种情况下对“推荐”实践的任何输入感兴趣。
回答by John Dibling
Virtuals may have defaults. The defaults in the base class are not inherited by derived classes.
虚拟机可能有默认值。派生类不继承基类中的默认值。
Which default is used -- ie, the base class' or a derived class' -- is determined by the static type used to make the call to the function. If you call through a base class object, pointer or reference, the default denoted in the base class is used. Conversely, if you call through a derived class object, pointer or reference the defaults denoted in the derived class are used. There is an example below the Standard quotation that demonstrates this.
使用哪个默认值——即基类或派生类——由用于调用函数的静态类型决定。如果通过基类对象、指针或引用调用,则使用基类中表示的默认值。相反,如果您通过派生类对象、指针或引用调用,则使用派生类中表示的默认值。标准引文下方有一个示例说明了这一点。
Some compilers may do something different, but this is what the C++03 and C++11 Standards say:
一些编译器可能会做一些不同的事情,但这就是 C++03 和 C++11 标准所说的:
8.3.6.10:
A virtual function call (10.3) uses the default arguments in the declaration of the virtual function determined by the static type of the pointer or reference denoting the object. An overriding function in a derived class does not acquire default arguments from the function it overrides. Example:
struct A { virtual void f(int a = 7); }; struct B : public A { void f(int a); }; void m() { B* pb = new B; A* pa = pb; pa->f(); //OK, calls pa->B::f(7) pb->f(); //error: wrong number of arguments for B::f() }
8.3.6.10:
虚函数调用 (10.3) 使用由表示对象的指针或引用的静态类型确定的虚函数声明中的默认参数。派生类中的覆盖函数不会从它覆盖的函数中获取默认参数。例子:
struct A { virtual void f(int a = 7); }; struct B : public A { void f(int a); }; void m() { B* pb = new B; A* pa = pb; pa->f(); //OK, calls pa->B::f(7) pb->f(); //error: wrong number of arguments for B::f() }
Here is a sample program to demonstrate what defaults are picked up. I'm using struct
s here rather than class
es simply for brevity -- class
and struct
are exactly the same in almost every way except default visibility.
这是一个示例程序,用于演示选择了哪些默认值。我使用的是struct
这儿,而不是class
简单地为了简洁ES -class
并且struct
是在除了默认的知名度几乎所有的方式完全一样。
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>
using std::stringstream;
using std::string;
using std::cout;
using std::endl;
struct Base { virtual string Speak(int n = 42); };
struct Der : public Base { string Speak(int n = 84); };
string Base::Speak(int n)
{
stringstream ss;
ss << "Base " << n;
return ss.str();
}
string Der::Speak(int n)
{
stringstream ss;
ss << "Der " << n;
return ss.str();
}
int main()
{
Base b1;
Der d1;
Base *pb1 = &b1, *pb2 = &d1;
Der *pd1 = &d1;
cout << pb1->Speak() << "\n" // Base 42
<< pb2->Speak() << "\n" // Der 42
<< pd1->Speak() << "\n" // Der 84
<< endl;
}
The output of this program (on MSVC10 and GCC 4.4) is:
这个程序的输出(在 MSVC10 和 GCC 4.4 上)是:
Base 42
Der 42
Der 84
回答by David Thornley
This was the topic of one of Herb Sutter's early Guru of the Weekposts.
这是赫伯·萨特 (Herb Sutter) 早期的《本周大师》帖子之一的主题。
The first thing he says on the subject is DON'T DO THAT.
他在这个问题上说的第一件事是不要那样做。
In more detail, yes, you can specify different default parameters. They won't work the same way as the virtual functions. A virtual function is called on the dynamic type of the object, while the default parameter values are based on the static type.
更详细地说,是的,您可以指定不同的默认参数。它们的工作方式与虚函数不同。在对象的动态类型上调用虚函数,而默认参数值基于静态类型。
Given
给定的
class A {
virtual void foo(int i = 1) { cout << "A::foo" << i << endl; }
};
class B: public A {
virtual void foo(int i = 2) { cout << "B::foo" << i << endl; }
};
void test() {
A a;
B b;
A* ap = &b;
a.foo();
b.foo();
ap->foo();
}
you should get A::foo1 B::foo2 B::foo1
你应该得到 A::foo1 B::foo2 B::foo1
回答by Oktalist
This is a bad idea, because the default arguments you get will depend on the statictype of the object, whereas the virtual
function dispatched to will depend on the dynamictype.
这是一个坏主意,因为您获得的默认参数将取决于对象的静态类型,而virtual
分派到的函数将取决于动态类型。
That is to say, when you call a function with default arguments, the default arguments are substituted at compile time, regardless of whether the function is virtual
or not.
也就是说,当你调用一个带有默认参数的函数时,默认参数会在编译时被替换,而不管该函数是否virtual
存在。
@cppcoder offered the following example in his [closed] question:
@cppcoder 在他的 [关闭]问题中提供了以下示例:
struct A {
virtual void display(int i = 5) { std::cout << "Base::" << i << "\n"; }
};
struct B : public A {
virtual void display(int i = 9) override { std::cout << "Derived::" << i << "\n"; }
};
int main()
{
A * a = new B();
a->display();
A* aa = new A();
aa->display();
B* bb = new B();
bb->display();
}
Which produces the following output:
产生以下输出:
Derived::5
Base::5
Derived::9
With the aid of the explanation above, it is easy to see why. At compile time, the compiler substitutes the default arguments from the member functions of the static types of the pointers, making the main
function equivalent to the following:
借助上面的解释,很容易看出原因。在编译时,编译器从指针的静态类型的成员函数中替换默认参数,使main
函数等效于以下内容:
A * a = new B();
a->display(5);
A* aa = new A();
aa->display(5);
B* bb = new B();
bb->display(9);
回答by Shital Shah
As other answers have detailed, its bad idea. However since no one mentions simple and effective solution, here it is: Convert your parameters to struct and then you can have default values to struct members!
正如其他答案所详述的那样,这是个坏主意。然而,由于没有人提到简单有效的解决方案,这里是:将您的参数转换为结构体,然后您可以为结构体成员设置默认值!
So instead of,
所以,而不是,
//bad idea
virtual method1(int x = 0, int y = 0, int z = 0)
do this,
做这个,
//good idea
struct Param1 {
int x = 0, y = 0, z = 0;
};
virtual method1(const Param1& p)
回答by Mark B
As you can see from the other answers this is a complicated subject. Instead of trying to do this or understand what it does (if you have to ask now, the maintainer will have to ask or look it up a year from now).
正如您从其他答案中看到的那样,这是一个复杂的主题。而不是尝试这样做或了解它的作用(如果您现在必须询问,维护人员将不得不在一年后询问或查找)。
Instead, create a public non-virtual function in the base class with default parameters. Then it calls a private or protected virtual function that has no default parameters and is overridden in child classes as needed. Then you don't have to worry about the particulars of how it would work and the code is very obvious.
相反,使用默认参数在基类中创建一个公共非虚拟函数。然后它调用一个没有默认参数的私有或受保护的虚函数,并根据需要在子类中被覆盖。然后你不必担心它如何工作的细节,代码非常明显。
回答by Jerry Coffin
This is one that you can probably figure out reasonably well by testing (i.e., it's a sufficiently mainstream part of the language that most compilers almost certainly get it right and unless you see differences between compilers, their output can be considered pretty well authoritative).
这是一个您可能可以通过测试合理地弄清楚的问题(即,它是该语言的一个足够主流的部分,大多数编译器几乎可以肯定它是正确的,除非您看到编译器之间的差异,否则它们的输出可以被认为是非常权威的)。
#include <iostream>
struct base {
virtual void x(int a=0) { std::cout << a; }
virtual ~base() {}
};
struct derived1 : base {
void x(int a) { std:: cout << a; }
};
struct derived2 : base {
void x(int a = 1) { std::cout << a; }
};
int main() {
base *b[3];
b[0] = new base;
b[1] = new derived1;
b[2] = new derived2;
for (int i=0; i<3; i++) {
b[i]->x();
delete b[i];
}
derived1 d;
// d.x(); // won't compile.
derived2 d2;
d2.x();
return 0;
}