虚函数是在C ++中实现运行时多态的唯一方法吗?

时间:2013-02-19 08:46:46

标签: c++ polymorphism

我的一位朋友问我“如何在C ++中实现运行时多态性?”我回答“通过继承”

他说“不,只能使用虚拟功能才能实现”。

所以我给了他一个以下代码的例子: -

#include<iostream>
using namespace std;

class A
{
public:
    int i;
    A(){i=100;}
};

class B : public A
{
public:
    int j;
    B(){i = -1; j = 99;}
};

void func(A& myA)
{
    cout<<myA.i << endl;
}

int main()
{
    B b;
    A* a = new B();
    func(*a);
    func(b);
    delete a;
    return 0;
}

这里,函数func()接受A的引用,但是我们传递B的对象,我们可以打印公共成员“i”的值。他说这是编译时多态性。

我的问题是: -

1)运行时多态性是否仅通过虚函数实现?

2)上面的例子是运行时多态还是编译时间?

3)如果我有以下代码: -

void func2(A& myA)
{
    cout << myA.i << endl;
    // dynamic/static cast myA to myB
    cout<<myB.j << endl;
}

它是什么样的多态性?或者甚至是多态?

6 个答案:

答案 0 :(得分:9)

该示例未显示动态多态性。要调用的方法在编译时是已知的。对于应该调用哪个方法,没有运行时决定(基于实际对象类型)。不同的类型没有不同的行为。

以示例为动态多态性的示例 您需要在Base类中提供virtual成员函数,并在派生类中覆盖它。要调用的实际方法由Base类指针指向的对象的实际类型决定。

<强> Online sample

#include<iostream>
using namespace std;

class A
{
public:
    virtual void doSomething()
    {
        std::cout<<"\nIn A::doSomething()";
    }
};

class B : public A
{
public:
    virtual void doSomething()
    {
        std::cout<<"\nIn B::doSomething()";
    }
};



int main()
{
    B b;
    A obj;
    A* a = &b;
    a->doSomething();

    a = &obj;
    a->doSomething();

    return 0;
}

输出

In B::doSomething()
In A::doSomething()

  

运行时多态性是仅通过虚拟函数实现的吗?

不,但virtual功能是最常见和最正确的方法 多态性可以通过函数指针实现。请考虑以下 code example ,根据用户输入,在运行时决定实际调用方法。它是一种多态性形式,不是严格意义上的C ++意义,它规定了不同类型的不同行为。

#include <iostream>

typedef void (*someFunction)(int, char*);

void FirstsomeFunction(int i, char *c)
{
    std::cout<<"\n In FirstsomeFunction";
}

void SecondsomeFunction(int i, char *c)
{
    std::cout<<"\n In SecondsomeFunction";
}

int main()
{
    someFunction arr[1];
    int x = 0;
    std::cin >> x;

    if(x ==0)
        arr[0] = &FirstsomeFunction;
    else
        arr[0] = &SecondsomeFunction;

    (arr[0])(10,"Hello");

    return 0;
}
  

以上示例是否具有运行时多态性或编译时间?

没有任何类型的多态性。在所有情况下都会调用相同的方法。不同的类型没有不同的行为,因此它不会被归类为任何类型的多态。

答案 1 :(得分:5)

C语言的fprintf是一个多态函数。

您可以传递各种句柄,它可以打印到文件,标准输出,打印机,a socket,系统可以表示为流的任何内容。

FILE* file = fopen("output.txt", "w");                    // a file
FILE* file = stdout;                                      // standard output
FILE* file = fopen("/dev/usb/lp0", "w");                  // a usb printer
FILE* file = popen("/usr/bin/lpr -P PDF", "w");           // a PDF file
FILE* file = fdopen(socket(AF_INET,SOCK_STREAM,0), "r+"); // network socket

fprintf(file, "Hello World.\n");

答案 2 :(得分:2)

你写的不是多态性。

这就是你在C ++中做多态的方法:

#include<iostream>
using namespace std;

class A
{
public:
    virtual void func(){
        cout << "printing A" << endl;
    }

    virtual ~A(){}
};

class B : public A
{
public:
    void func(){
        cout << "printing B" << endl;
    }
};

int main()
{
    A* a = new A();
    A* b = new B();

    a->func(); // "printing A"
    b->func(); // "printing B"

    delete a;
    delete b;

    return 0;
}

如果您要删除虚拟关键字,A的方法func将被调用两次。

答案 3 :(得分:1)

  

我的一位朋友问我“如何在C ++中实现运行时多态性?”我回答“通过继承”   他说:“不,只能使用虚拟功能才能实现”。

首先,术语多态是不明确的:在通用计算科学意义上,它指的是隐式调用特定于类型的代码的能力,无论是在编译时还是在运行时。在C ++标准中,定义非常狭窄的是虚拟调度(这是标准的普遍性)。显然,对于你朋友的问题是有意义的,因为他在询问它是如何在C ++中实现的,他的观点必须来自C ++外部 - 在计算科学术语的更大范围内。

当然,虚拟函数/调度是 答案,但它们只是 答案吗?

为了尝试回答这个问题,有必要清楚地了解哪些行为符合运行时多态性。考虑:

void f(X& x)
{
    // the only situation where C++ doesn't know the run-time type of a variable is
    // where it's an instance of a class/struct accepted by reference or pointer

    ...some operation involving "x"...
}

任何可能导致调用涉及“x”的操作的机器代码的机制,其中原因与运行时类型“x”特别相关,是非常接近到运行时多态,但最后一个问题是:分支是由语言隐式决定的,还是程序员明确排列的?

在虚拟分派的情况下,编译器隐式知道创建分支到类型相应代码的虚拟分派表和查找。

但是,假设我们有一个函数指针,它先前已设置为适合类型的代码,或者是特定于类型的数字或枚举,用于控制switch到类型特定的{{1} }。这些功能实现与运行时虚拟分派相同的行为,但设置必须由开发人员明确完成,并且没有编译器强制执行以确保完全基于运行时类型进行确定。他们是否合格是有争议的。因为C ++在虚拟调度中具有完全隐式的机制,并且因为在C ++标准中,多态性具有与虚拟调度相关的缩小定义,所以我猜大多数C ++程序员会说“不”。

但在C世界中,描述说caseqsort(两个标准的libC函数,通过函数指针参数使用运行时调度处理任意类型)作为运行时多态可能有助于快速理解......虽然说它们是泛型实现更为正常。

尽管如此,毫无疑问,有数百本计算科学教科书都有运行时多态性的功能定义,我敢打赌使用函数指针或其他程序员初始化的元数据进行调度可以满足它们的很大比例。因此,过于坚持没有一个明确的答案是毫无意义的。

  

我的问题是: -

     

1)运行时多态性是否仅通过虚函数实现?

如上所述,我在C ++的背景下倾向于“是”,但它(无休止地)可论证。

2)上面的例子是运行时多态还是编译时间?

两者都没有...在类型的基础上甚至没有两个函数可供选择 - 你总是为bsearch运行相同的机器代码:编译器选择的机器代码期望类型为{ {1}}。

  

3)如果我有以下代码: -

func()
  

它是什么样的多态性?或者甚至是多态?

根本不具有多态性,因为您没有基于类型的分支。动态强制转换可以参考myA的运行时类型中的编译器填充类型元数据,如果您使用它只是有条件地调用对A的访问 - 这将是未定义的行为,除非{{1} }是一个void func2(A& myA) { cout << myA.i << endl; // dynamic/static cast myA to myB cout<<myB.j << endl; } - 然后你回到手动,显式开发人员协调的类型特定的行为,以及是否有资格作为“多态”为上述。

答案 4 :(得分:0)

使用虚函数实现多态性。但是要产生任何效果,即根据类型不同的行为,你也需要继承

struct A {
    virtual void f() = 0;
};

struct B : public A {
    void f() {
        // do B things
        std::cout << "B::f() called\n";
    }
};

struct C : public A {
    void f() {
        // do C things
        std::cout << "C::f() called\n";
    }
};

现在,您可以使用不同行为的A指针或引用,具体取决于它是B还是C

答案 5 :(得分:0)

[C ++]

多态性被定义为控制对一般操作类的访问的一个接口。有两种类型的多态性,一种是编译时多态,另一种是运行时多态。编译时多态是函数和运算符重载。运行时多态性使用继承和虚函数完成。

多态性意味着函数在不同的时间呈现不同的形式。在编译时,它被称为函数重载。例如,程序可以包含两个函数,其中一个可以执行整数加法,另一个可以执行浮点数的添加,但函数的名称可以相同,例如add。函数add()被认为是重载的。两个或多个函数可以具有相同的名称,但它们的参数列表在参数或其数据类型方面应该不同。仅在返回类型方面不同的函数不能重载。编译器将根据传递的参数类型选择正确的函数。在类的情况下,构造函数可能会被重载,因为可以有初始化和未初始化的对象。