有关虚拟和默认参数的问题

时间:2011-07-21 08:54:05

标签: c++ virtual override

我想确认以下事项:

虚拟机制:

我有一个基类A,它有一个Virtual方法,然后在派生类中,我们不在函数声明中包含虚拟语句。但是,当包含在衍生类定义中时虚拟意味着什么。

class A
{
public: 
virtual void something();
}

class B:public A
{
public:
virtual void something();
}

是否,这意味着我们想要在从类B派生的类中重写一些方法?

另外,另一个问题是,

我有一个类A,它由三个不同的类派生。现在,在基类A中有一个虚方法anything()。

现在,如果我要在基类A :: anything()中为该方法添加一个新的默认参数,我需要将它添加到所有3个类中。

我选择答案:

  1. 如果在派生类中将基类中的虚拟方法重新定义为虚拟,那么我们可能意味着它应该在使用此类作为基类的相应派生类中重写。
  2. 是的。如果没有超越则没有任何意义。
  3. 请告诉我我的感受(2以上)是否正确。

    谢谢, Pavan Moanr。

4 个答案:

答案 0 :(得分:2)

派生类中的覆盖可以省略virtual关键字。如果基类中的重写函数是虚拟的,则假定覆盖也是虚拟的。

这个问题很清楚:In C++, is a function automatically virtual if it overrides a virtual function?


您的第二个问题是关于默认值和虚函数。基本上,每个覆盖可以具有不同的默认值。但是,通常这不符合您的预期,所以我的建议是:不要混合默认值和虚函数

基类函数是否默认,完全独立于派生类函数是否默认。

基本思想是静态类型将用于查找默认值(如果已定义)。对于虚函数,动态类型将用于查找被调用函数。

因此,当动态和静态类型不匹配时,将出现意外结果。

e.g。

#include <iostream>

class A
{
public:
  virtual void foo(int n = 1) { std::cout << "A::foo(" << n << ")" << std::endl; }
};

class B : public A
{
public:
  virtual void foo(int n = 2) { std::cout << "B::foo(" << n << ")" << std::endl; }
};

int main()
{
  A a;
  B b;

  a.foo(); // prints "A::foo(1)";
  b.foo(); // prints "B::foo(2)";

  A& ref = b;
  ref.foo(); // prints "B::foo(1)";
}

如果所有覆盖都共享相同的默认值,另一种解决方案是在基类中定义一个除了使用默认参数调用虚函数之外什么都不做的附加函数。那就是:

class A
{
public:
   void defaultFoo() { foo(1); }
   virtual void foo(int n) { .... }
};

如果您的覆盖具有不同的默认值,则有两个选项:

  • 同样使defaultFoo()虚拟,如果派生类超载一个而不是另一个,则可能导致意外结果。
  • 不使用默认值,但在每次调用中明确说明使用的值。

我更喜欢后者。

答案 1 :(得分:0)

是否在派生类中编写virtual并不重要,由于基类,它始终是虚拟的,但是最好包含virtual来明确声明它是虚拟的,然后如果你不小心从基类中删除了该关键字,它将给你编译错误(你不能用虚拟函数重新定义非虚函数)。编辑&gt;&gt;对不起我错了。您可以使用虚拟函数重新定义非虚函数,但是一旦它是虚拟函数,即使您不编写虚拟关键字,所有具有相同签名的派生类函数也将是虚拟函数。 &LT;&LT;

如果不重新定义虚函数,则将使用基类中的定义(就像它被逐字复制一样)。

如果您希望指定应在dervied类中重新定义虚函数,则不应提供任何实现,即virtual void something() = 0; 在这种情况下,您的类将是一个抽象基类(ABC),并且不能从中实例化任何对象。如果派生类没有提供它自己的实现,那么它也将是一个ABC。

我不确定你对默认参数的意思是什么,但是函数签名应该匹配所以所有参数和返回值应该是相同的(最好不要将重载/默认参数与继承混合在一起,因为你可以得到非常令人惊讶的结果例如:

class A
{
public: 
void f(int x);
};

class B:public A
{
public:
void f(float x);
};

int main() {
B b;
b.f(42); //this will call B::f(float) even though 42 is int
}

答案 2 :(得分:0)

这是一个小实验来测试你想知道的内容:

class A {
public:
        virtual void func( const char* arg = "A's default arg" ) {
                cout << "A::func( " << arg << " )" << endl;
        }
};

class B : public A {
public:
        void func( const char* arg = "B's default arg" ) {
                cout << "B::func( " << arg << " )" << endl;
        }
};

class C : public B {
public:
        void func( const char* arg ) {
                cout << "C::func( " << arg << " )" << endl;
        }
};

int main(int argc, char* argv[])
{
        B* b = new B();
        A* b2 = b;
        A* c = new C();
        b->func();
        b2->func();
        c->func();
        return 0;
}

结果:

B::func( B's default arg )
B::func( A's default arg )
C::func( A's default arg )

结论:

在A的func声明之前,

1- virtual关键字使该函数在B和C中也是虚拟的。

2-使用的默认参数是在用于访问对象的指针/引用类中声明的参数。

答案 3 :(得分:0)

正如有人所指出的,派生类中与基类中的虚函数具有相同名称和类型签名的函数自动始终是虚函数。

但是关于默认参数的第二个问题很有意思。这是一个思考问题的工具......

class A {
  public:
   virtual void do_stuff_with_defaults(int a = 5, char foo = 'c');
};

几乎相当于:

class A {
  public:
   virtual void do_stuff_with_defaults(int a, char foo);

   void do_stuff_with_defaults() { // Note lack of virtual keyword
      do_stuff_with_defaults(5, 'c'); // Calls virtual function
   }

   void do_stuff_with_defaults(int a) { // Note lack of virtual keyword
      do_stuff_with_defaults(a, 'c'); // Calls virtual functions
   }
};

因此,如果给出虚函数默认参数,那么基本上你的虚函数和非虚函数具有相同的名称但在类中声明的类型签名不同。

在途中它不等同于能够使用using指令从基类导入名称。如果将默认参数声明为单独的函数,则可以使用using指令导入这些函数。如果您只是声明默认参数,则不是。