10

我的一位朋友问我“如何在 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;
}

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

4

6 回答 6

9

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

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

在线样本

#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函数是最常见和最正确的方法。
多态可以通过函数指针来实现。考虑下面的代码示例,调用的实际方法是在运行时根据用户输入决定的。它是一种多态性形式,不是严格意义上的 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;
}

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

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

于 2013-02-19T08:51:13.737 回答
7

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

您可以向它传递各种句柄,它可以打印到文件、标准输出、打印机、套接字以及系统可以表示为流的任何东西。

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");
于 2013-02-19T09:01:20.980 回答
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;
}

如果要删除 virtual 关键字,则funcA 的方法将被调用两次。

于 2013-02-19T08:57:42.530 回答
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”的运行时类型特别相关,都非常接近运行时多态性,但还有一个最后一个问题:分支是由语言隐式决定的,还是由程序员明确安排的?

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

但是,假设我们有一个函数指针,该指针以前设置为寻址适合类型的代码,或者用于控制 aswitch到特定类型的特定类型数字或枚举case。这些在功能上实现了与运行时虚拟分派相同的行为,但设置必须由开发人员显式完成,并且没有编译器强制执行来确保纯粹在运行时类型上完成确定。他们是否有资格是有争议的。因为 C++ 在虚拟分派中具有完全隐式的机制,并且因为在 C++ 标准中多态性对虚拟分派的定义很窄,所以我猜大多数 C++ 程序员会说“不”。

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

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

我的问题是:-

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

如上所述,在 C++ 的上下文中,我倾向于“是”,但它(无休止地)有争议。

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

也没有......甚至没有两个函数可以根据类型选择 - 你总是运行相同的机器代码func():编译器选择的机器代码假设类型是A.

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

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

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

根本不是多态的,因为您没有基于类型的分支。动态转换可以在 myA 的运行时类型中查询编译器填充的类型元数据,如果您使用它来仅有条件地调用访问myB.j- 这将是未定义的行为,除非myA是 a B- 那么你回到手动、明确地开发人员协调特定类型的行为,以及上面讨论了这是否符合您的“多态性”。

于 2013-02-19T10:52:23.273 回答
0

[C++]

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

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

于 2013-02-19T10:47:37.390 回答
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具有不同行为的指针或引用,具体取决于它是 aB还是C.

于 2013-02-19T08:56:44.057 回答