微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

C++:自定义 vtable 实现不起作用

如何解决C++:自定义 vtable 实现不起作用

我正在尝试实现自定义 vtable 以更好地理解虚拟表和覆盖的概念。为此,我有以下“基础”类

#pragma once

#include <iostream>
#include <string>

using namespace std::string_view_literals;

struct vtable;

class IdentityDocument {
public:
    IdentityDocument()
        : vtable_ptr_(&IdentityDocument::VTABLE),unique_id_(++unique_id_count_)
    {
        std::cout << "IdentityDocument::Ctor() : "sv << unique_id_ << std::endl;
    }

    ~IdentityDocument() {
        --unique_id_count_;
        std::cout << "IdentityDocument::Dtor() : "sv << unique_id_ << std::endl;
    }

    IdentityDocument(const IdentityDocument& other)
        : vtable_ptr_(other.vtable_ptr_),unique_id_(++unique_id_count_)
    {
        std::cout << "IdentityDocument::CCtor() : "sv << unique_id_ << std::endl;
    }

    IdentityDocument& operator=(const IdentityDocument&) = delete;

    void PrintID() const {
        std::cout << "IdentityDocument::PrintID() : "sv << unique_id_ << std::endl;
    }

    static void PrintUniqueIDCount() {
        std::cout << "unique_id_count_ : "sv << unique_id_count_ << std::endl;
    }

    int GetID() const {
        return unique_id_;
    }

private:
    vtable* vtable_ptr_ = nullptr;

    static int unique_id_count_;
    static vtable VTABLE;
    int unique_id_;
};

struct vtable
{
    void (IdentityDocument::* const PrintID)() const;

    vtable (
        void (IdentityDocument::* const PrintID)() const
    ) : PrintID(PrintID) {}
};

int IdentityDocument::unique_id_count_ = 0;
vtable IdentityDocument::VTABLE = {&IdentityDocument::PrintID};

这是另一个必须覆盖 PrintId 方法的类

#pragma once

#include "identity_document.h"
#include <iostream>
#include <string>
#include <ctime>

using namespace std::string_view_literals;

class Passport {
public:
    Passport()
        : expiration_date_(GetExpirationDate())
    {
        IdentityDocument* base_ptr = reinterpret_cast<IdentityDocument*>(this);
        vtable* vtable_ptr = reinterpret_cast<vtable*>(base_ptr);
        vtable_ptr = &Passport::VTABLE;

        std::cout << "Passport::Ctor()"sv << std::endl;
    }

    Passport(const Passport& other)
        : identity_(other.identity_),expiration_date_(other.expiration_date_)
    {
        IdentityDocument* base_ptr = reinterpret_cast<IdentityDocument*>(this);
        vtable* vtable_ptr = reinterpret_cast<vtable*>(base_ptr);
        vtable_ptr = &Passport::VTABLE;

        std::cout << "Passport::CCtor()"sv << std::endl;
    }

    ~Passport() {
        std::cout << "Passport::Dtor()"sv << std::endl;
    }

    void PrintID() const {
        std::cout << "Passport::PrintID() : "sv << identity_.GetID();
        std::cout << " expiration date : "sv << expiration_date_.tm_mday << "/"sv << expiration_date_.tm_mon << "/"sv
                  << expiration_date_.tm_year + 1900 << std::endl;
    }

    void PrintVisa(const std::string& country) const {
        std::cout << "Passport::PrintVisa("sv << country << ") : "sv << identity_.GetID() << std::endl;
    }

private:
    IdentityDocument identity_;
    const struct tm expiration_date_;
    static vtable VTABLE;

    tm GetExpirationDate() {
        time_t t = time(nullptr);
        tm exp_date = *localtime(&t);
        exp_date.tm_year += 10;
        mktime(&exp_date);
        return exp_date;
    }
};

vtable Passport::VTABLE = {reinterpret_cast<void (IdentityDocument::*)() const>(&Passport::PrintID)};

还有一个简短的演示:

int main() {
    array<IdentityDocument*,1> docs = { (IdentityDocument*)(new Passport()) };
    for (const auto* doc : docs) {
        doc->PrintID();
    }
}

不幸的是,我看到未调用“派生”方法。我是否使用了错误方法来实现 vtable 概念?

解决方法

我是否使用了错误的方法来实现 vtable 概念?

是的。您还没有编写任何读取 vtable 的代码,C++ 编译器也不会生成任何代码来读取您的 vtable。

当您声明成员函数 virtual 时,您的编译器需要以特殊方式调用该函数。对该函数的任何调用都应该在 vtable 中查找

当成员函数not virtual 时,您的编译器知道它不需要查找函数的位置。它知道要调用哪个函数。无需查找。

在您的代码中,您创建了一个 vtable,但是这一行调用了一个非虚拟函数:

doc->PrintID();

不需要 vtable,也不需要检查。

doc 是一个 IdentityDocument*,所以 doc->PrintID() 调用 IdentityDocument::PrintID()。无需查找,无需查找。

,

最后我简化了我的解决方案并得到了我想要的:

#include <iostream>
class A;
struct VTable
{
    void (*say_hello)(A*);
};
class A
{
public:
    A()
    {
        vtable.say_hello = A::sayHello;
    }

    void sayHello()
    {
        vtable.say_hello(this);
    }
    static void sayHello(A* a)
    {
        std::cout << "A::sayHello" << std::endl;
    }
    VTable vtable;
};
class B
{
public:
    B()
    {
        a.vtable.say_hello = B::sayHello;
    }
    void sayHello()
    {
        a.vtable.say_hello((A*)this);
    }
    static void sayHello(A* a)
    {
        std::cout << "B::sayHello\n" << std::endl;
    }
private:
    A a;
};

int main()
{
    A* a = (A*)(new B);
    a->sayHello();
    delete a;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。