【问题标题】:Virtual Functions C++: virtual function already has a body虚函数 C++:虚函数已经有一个主体
【发布时间】:2015-11-04 14:26:46
【问题描述】:

我在实现一个在另外两个类的抽象类中声明的虚函数时遇到了麻烦,在不同的头文件中。

当我在类 ProtocolLogin 中实现已经在 ProtocolGame 中实现的虚函数“parsePacket”时,编译器返回“函数已经有一个主体”。

Error   1   error LNK2005: "private: virtual void __cdecl ProtocolGame::parsePacket(class NetworkMessage &)" (?parsePacket@ProtocolGame@@EEAAXAEAVNetworkMessage@@@Z) already defined in protocolgame.obj

Error   2   error LNK2001: unresolved external symbol "public: virtual void __cdecl ProtocolLogin::parsePacket(class NetworkMessage &)" (?parsePacket@ProtocolLogin@@UEAAXAEAVNetworkMessage@@@Z)

然后我尝试创建一个名为 parseWater 的新虚拟函数,它将仅在 Class ProtocolLogin 中实现,编译器返回“函数需要在 ProtocolGame 声明”,但如果我这样做,我会再次得到:“函数已经有了身体”。所以,我只是不再关注了。请帮助我理解:)

它就像一个无限循环。

    class Protocol (Header File 1)
{
    public:
        explicit Protocol(Connection_ptr connection) : m_connection(connection)
        virtual ~Protocol() = default;

        // non-copyable
        Protocol(const Protocol&) = delete;
        Protocol& operator=(const Protocol&) = delete;

        virtual void parsePacket(NetworkMessage&) {}
        void onRecvMessage(NetworkMessage& msg); // Function that calls parsePacket
        virtual void onRecvFirstMessage(NetworkMessage& msg) = 0; 

class ProtocolGame final : public Protocol (Header File 2)
{
    public:
        // static protocol information
        enum {server_sends_first = true};
        enum {protocol_identifier = 0}; // Not required as we send first
        enum {use_checksum = true};
        static const char* protocol_name() {
            return "gameworld protocol";
        }

        explicit ProtocolGame(Connection_ptr connection);

            private:
        void parsePacket(NetworkMessage& msg) final; //implementation //Works
        void onRecvFirstMessage(NetworkMessage& msg) final; //implementation //Works

     class ProtocolLogin final : public Protocol (Header File 3)
{
    public:
        // static protocol information
        enum {server_sends_first = false};
        enum {protocol_identifier = 0x01};
        enum {use_checksum = true};
        static const char* protocol_name() {
            return "login protocol";
        }

        explicit ProtocolLogin(Connection_ptr connection) : Protocol(connection) {}

        void onRecvFirstMessage(NetworkMessage& msg); //work
        void parsePacket(NetworkMessage& msg); // dont work

【问题讨论】:

  • 不清楚真正的代码是什么,真正的错误是什么。请按原样提供代码,并按报告错误的行放置错误。顺便说一句,在代码中添加 final 的目的是什么?我相信,这是一种不好的做法。
  • 一方面,void onRecvLiquid(); 不是虚拟的
  • MCVE.
  • parseAlcohol() 也不是,所以你把它指定为 final 是不正确的。
  • 我已经编辑并更改了我在这里引用时拼错的功能。对不起。现在是正确的。请大家复习一下:)

标签: c++ virtual


【解决方案1】:

parseWater 是唯一声明为final 的最终函数。

所有其他 final 函数在某处没有 virtual 关键字。

正如 StoryTeller 指出的那样“您不必在派生类中重述虚拟说明符。”

【讨论】:

  • 我已经编辑并更改了我在这里引用时拼错的功能。对不起。现在是正确的。请检查一下:)
  • 你为什么拒绝我的编辑?您的代码仍然不可编译且难以阅读……
  • 修复了明显的错误(分号、括号等)后,新版本适用于我(MSVC2015) - 请参阅我的代码编辑
  • 抱歉,西蒙,正如 SergeyA 所建议的那样,我正在更新它以使其看起来像在我的身上。你能再看一遍吗?
【解决方案2】:

我对 C++11 不是很熟悉,但是想要实现虚函数的派生类也需要将它们的实现声明为virtual。我不认为final 消除了声明virtual 的需要。 (事实上​​,将非虚函数声明为final是错误的)

如果您注意到,您的父类定义了 parseWater()virtual,但没有定义 parseAlchohol(),这就是为什么您的 ProtocolAlchohol 类在声明 void parseAlchohol() final;ProtocolWater 时没有问题void parseWater() final;的问题

p.s.,如果您在父类中明确引用子类概念(即水和酒精)以及基于水和酒精的命名函数,我不确定您是否得到了正确的推导和 OOP。

编辑:别介意我的第一段。我想我总是把它作为一种惯例,并没有意识到这没有必要。

【讨论】:

  • 你没有在派生类中重述虚拟说明符。
  • “你的派生类想要实现虚函数也需要将它们的实现声明为虚函数。”不正确。
  • 最后的说明符“指定一个虚函数不能在派生类中被覆盖或者一个类不能被继承。”。这可能用于在基类中声明公共函数,但保护它们不会在派生类中被覆盖。 (en.cppreference.com/w/cpp/language/final)
  • 我已经编辑并更改了我在这里引用时拼错的功能。对不起。现在是正确的。请检查一下:)
【解决方案3】:

既然我们正在讨论在派生类中使用virtual,我想声明一下。不幸的是,在我看来,C++ 在这方面是不一致的。您可能会或可能不会“重述”虚拟。更糟糕的是,当一个函数在基类中不是虚拟时,您可以将其设为虚拟 - 这可能会给代码阅读器一个函数是虚拟的指示 - 一个错误的指示。

如果 C++ 真的强制使用 virtual 对代码维护者来说会容易得多——在所有虚拟函数的重新定义中都要求它,并在非虚拟函数的重新定义中禁止它。

【讨论】:

  • 我已经编辑并更改了我在这里引用时拼错的功能。对不起。现在是正确的。请检查一下:)
  • 这些对于语言流利的程序员来说都不是问题。
  • @StoryTeller,真的吗?非问题? ` 结构 X { };结构 foo : Base { void f(); };` f() 是虚拟的吗?
  • @SergeyA,如果没有调用点和Base<> 的定义,就无法回答。但是不熟悉使用的类和所涉及的语言机制并不是语言的问题。你的例子是无关紧要的。
  • @StoryTeller,我明白了,你没有明白这一点。 Base 有多个特化。 struct X 的那个有 f() 虚拟的吗?我所知道的任何 IDE 都不能指向模板类的特定专业化。只是一点建议。对于一个自称“刚从大学毕业”的人来说,你有点太大胆了。尝试了解拥有 20 多年专业 C++ 经验的人所说的话,您会受益匪浅;)
猜你喜欢
  • 2011-07-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-03
  • 2023-03-12
  • 2012-08-27
  • 1970-01-01
相关资源
最近更新 更多