C++抽象基类构造函数/析构函数 - 一般正确性

发布于 2024-12-21 02:00:07 字数 875 浏览 1 评论 0原文

我想要一个 C++ Interface ,在继承时必须重写它(如果可能的话)。到目前为止,我有以下内容:

class ICommand{

public:
    //  Virtual constructor. Needs to take a name as parameter
    //virtual ICommand(char*) =0;
    //  Virtual destructor, prevents memory leaks by forcing clean up on derived classes?
    //virtual ~ICommand() =0; 
    virtual void CallMe() =0;
    virtual void CallMe2() =0;
};

class MyCommand : public ICommand
{
public:
    // Is this correct?
    MyCommand(char* Name) { /* do stuff */ }
    virtual void CallMe() {}
    virtual void CallMe2() {}
};

我故意留下了我认为应该如何在 ICommand 中实现构造函数/析构函数。我知道如果删除注释,它将无法编译。请有人:

  1. 告诉我如何在 ICommand 中声明构造函数/析构函数以及如何在 MyCommand 中使用它们
  2. 我是否在 ICommand 中正确设置了内容 以便 MyCommand 必须覆盖 CallMeCallMe2

I would like to have a C++ Interface that must be overridden (if this is possible) when inherited. So far, I have the following:

class ICommand{

public:
    //  Virtual constructor. Needs to take a name as parameter
    //virtual ICommand(char*) =0;
    //  Virtual destructor, prevents memory leaks by forcing clean up on derived classes?
    //virtual ~ICommand() =0; 
    virtual void CallMe() =0;
    virtual void CallMe2() =0;
};

class MyCommand : public ICommand
{
public:
    // Is this correct?
    MyCommand(char* Name) { /* do stuff */ }
    virtual void CallMe() {}
    virtual void CallMe2() {}
};

I have purposely left how I think the constructor/destructor's should be implemented in ICommand. I know if I remove the comments, it will not compile. Please could someone:

  1. Show me how to declare the constructor/destructor's in ICommand and how they are meant to be used in MyCommand
  2. Have I set things up correctly in ICommand so that MyCommand must override CallMe and CallMe2.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

于我来说 2024-12-28 02:00:07

C++ 不允许使用虚拟构造函数。一个简单的实现(没有虚拟构造函数)看起来像这样:

class ICommand {
public:
    virtual ~ICommand() = 0;
    virtual void callMe() = 0;
    virtual void callMe2() = 0;
};

ICommand::~ICommand() { } // all destructors must exist

请注意,即使是纯虚拟析构函数 必须定义。

具体的实现看起来与您的示例完全相同:

class MyCommand : public ICommand {
public:
    virtual void callMe() { }
    virtual void callMe2() { }
};

您有几个

#include <string>

class ICommand {
private:
    const std::string name;
    ICommand();
public:
    ICommand(const std::string& name) : name(name) { }
    virtual ~ICommand() = 0;
    virtual void callMe() = 0;
    virtual void callMe2() = 0;
};

ICommand::~ICommand() { } // all destructors must exist

具体实现现在如下所示:

class MyCommand : public ICommand {
public:
    MyCommand(const std::string& name) : ICommand(name) { }
    virtual void callMe() { }
    virtual void callMe2() { }
};

C++ does not allow for virtual constructors. A simple implementation (without the virtual constructor) would look something like this:

class ICommand {
public:
    virtual ~ICommand() = 0;
    virtual void callMe() = 0;
    virtual void callMe2() = 0;
};

ICommand::~ICommand() { } // all destructors must exist

Note that even a pure virtual destructor must be defined.

A concrete implementation would look exactly like your example:

class MyCommand : public ICommand {
public:
    virtual void callMe() { }
    virtual void callMe2() { }
};

You have a couple of options for the constructor. One option is to disable the default constructor for ICommand, so that subclasses will have to implement a constructor that calls your ICommand constructor:

#include <string>

class ICommand {
private:
    const std::string name;
    ICommand();
public:
    ICommand(const std::string& name) : name(name) { }
    virtual ~ICommand() = 0;
    virtual void callMe() = 0;
    virtual void callMe2() = 0;
};

ICommand::~ICommand() { } // all destructors must exist

A concrete implementation would now look something like this:

class MyCommand : public ICommand {
public:
    MyCommand(const std::string& name) : ICommand(name) { }
    virtual void callMe() { }
    virtual void callMe2() { }
};
深巷少女 2024-12-28 02:00:07

我知道这个已经很旧了,但这仍然是我第一次遇到这个问题。我就是这样做的。

接口头 foo.h:

#pragma once
#include <memory>

enum class Implementations {Simple, Fancy};

class Foo
{
public:
    using Ptr = std::unique_ptr<Foo>;
    virtual ~Foo() = default;
    virtual void do_it() = 0;
};

Foo::Ptr create_foo(Implementations impl); // factory

是的,我知道“pragma Once”严格来说不是标准的,但它对我有用。

请注意,这里没有执行任何操作。没有构造函数:抽象类无法实例化。您可以通过工厂获得指向接口的指针。为了使虚拟函数调用起作用,必须通过指针来调用它们。虚拟析构函数是默认的,因为除了实现的多态化之外,它不需要做任何特殊的事情。工厂是一个免费功能。无需尝试将其设为静态成员或类似的成员。这不是java。

接口foo.cpp:

#include "foo.h"
#include "foo_impl.h"

Foo::Ptr create_foo(Implementations impl)
{
    switch (impl)
    {
    case Implementations::Simple:
        return std::make_unique<Simple_foo>();
    case Implementations::Fancy:
        return std::make_unique<Fancy_foo>();
    default:
        return nullptr;
    }
}

这里实现了工厂。请注意,工厂必须知道实现。这就是为什么我们不内联实现它:如果它是内联的,接口标头必须包含实现标头,并且通过它,实现的知识将“泄漏”到调用站点。

实现头foo_impl.h:

#pragma once
#include "foo.h"

class Simple_foo : public Foo
{
    void do_it() override;
};

class Fancy_foo : public Foo
{
    void do_it() override;
};

没什么特别的,只是重写了接口的虚函数。因为这个例子很简单,所以我将两个实现放在同一个文件中。在实际应用中情况会有所不同。

实现 foo_impl.cpp:

#include "foo_impl.h"
#include <iostream>

void Simple_foo::do_it()
{
    std::cout << "simple foo\n";
}

void Fancy_foo::do_it()
{
    std::cout << "fancy foo\n";
}

只需实现函数即可。

main.cpp:

#include "foo.h"

int main()
{
    auto sf = create_foo(Implementations::Simple);
    sf->do_it();
    auto ff = create_foo(Implementations::Fancy);
    ff->do_it();
    return 0;
}

通过枚举我们可以选择我们想要的实现。这些指针的类型为 Foo::Ptr,它是 std::unique_ptr 的别名。调用点根本不知道实现,只知道接口。

输出将如预期:

simple foo
fancy foo

I know this one is old, but it is still my first hit on this issue. This is how I would do it.

Interface header foo.h:

#pragma once
#include <memory>

enum class Implementations {Simple, Fancy};

class Foo
{
public:
    using Ptr = std::unique_ptr<Foo>;
    virtual ~Foo() = default;
    virtual void do_it() = 0;
};

Foo::Ptr create_foo(Implementations impl); // factory

Yes I know that "pragma once" is strictly speaking not standard, but it works for me.

Note that nothing is implemented here. There is no constructor: an abstract class can not be instantiated. You get a pointer to the interface through the factory. For the virtual function calls to work, they must be called through a pointer. The virtual destructor is defaulted because it doesn't have to do anything special except polymorphing to the implementation. The factory is a free function. No need to try to make it a static member or something like that. This is not java.

Interface foo.cpp:

#include "foo.h"
#include "foo_impl.h"

Foo::Ptr create_foo(Implementations impl)
{
    switch (impl)
    {
    case Implementations::Simple:
        return std::make_unique<Simple_foo>();
    case Implementations::Fancy:
        return std::make_unique<Fancy_foo>();
    default:
        return nullptr;
    }
}

Here the factory is implemented. Notice that the factory has to know the implementation(s). That is why we don't implement it inline: if it was inline, the interface header would have to include the implementation header, and through it, knowledge of the implementation would "leak out" to the callsite.

The implementation header foo_impl.h:

#pragma once
#include "foo.h"

class Simple_foo : public Foo
{
    void do_it() override;
};

class Fancy_foo : public Foo
{
    void do_it() override;
};

Nothing special, just override the virtual functions of the interface. Because this exaple is simple, I put both implementations in the same files. In real applications that will be different.

The implementation foo_impl.cpp:

#include "foo_impl.h"
#include <iostream>

void Simple_foo::do_it()
{
    std::cout << "simple foo\n";
}

void Fancy_foo::do_it()
{
    std::cout << "fancy foo\n";
}

Just implement the functions.

The main.cpp:

#include "foo.h"

int main()
{
    auto sf = create_foo(Implementations::Simple);
    sf->do_it();
    auto ff = create_foo(Implementations::Fancy);
    ff->do_it();
    return 0;
}

Through the enum we can select the implementation we want. The pointers are of type Foo::Ptr, an alias for std::unique_ptr<Foo>. The callsite has no knowledge of the implementation at all, only the interface.

The output will be as expected:

simple foo
fancy foo
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文