【问题标题】:How to implement a class template that provides functionality to its derived classes to track their instances?如何实现为其派生类提供功能以跟踪其实例的类模板?
【发布时间】:2021-01-12 17:48:02
【问题描述】:

我正在尝试创建一个抽象类模板 (InstanceTracker),如果类需要对其所有实例执行操作的功能,则可以从该模板继承。该类包含指向“T”的指针的静态向量,并且每次运行 InstanceTracker 构造函数时,我都会推回一个指向该向量的新指针。我通过返回“T*”的纯虚拟 getDerivedPtr() 方法执行此操作,从 InstanceTracker 派生的每个类都必须使用 return this; 实现。不过,您可能已经看到出了什么问题。您永远不能从基本构造函数调用纯虚函数——因为它还不存在。如何为我的 InstanceTracker 类找到解决此问题的方法?这是该类的代码:

#pragma once
#include <vector>

template <typename T>
class InstanceTracker
{
public:
    InstanceTracker() noexcept
    {
        allInstances_.push_back(getDerivedPtr());
    }
    InstanceTracker(const InstanceTracker& source) noexcept
        : InstanceTracker()
    {
    }
    InstanceTracker(const InstanceTracker&& source) noexcept
        : InstanceTracker()
    {
    }
    virtual ~InstanceTracker() noexcept
    {
        auto it = std::find(allInstances_.begin(), allInstances_.end(), this);
        int index = it - allInstances_.begin();
        allInstances_.erase(allInstances_.begin() + index);
    }
    virtual T* getDerivedPtr() = 0;
protected:
    static std::vector<T*> allInstances_;
};

如果你想尝试运行代码,看看它为什么现在不起作用,这里有一个继承自 InstanceTracker 的简单类:

class Derived1 : public InstanceTracker
{
public:
    Derived1* getDerivedPtr() override
    {
        return this;
    }
};

【问题讨论】:

  • 将指针 (this) 传递给 InstanceTracker 的 ctor?
  • en.wikipedia.org/wiki/… 看起来与维基百科的示例非常相似。 stackoverflow.com/questions/4173254/…
  • 我只想static_cast c'tor 中的this 指针。 IIRC 只要以有限的方式使用指针,获取指向正在构建的对象的指针是合法的。仅仅存储它就是这样一种方式。
  • getDerivedPtr 不需要是虚拟的。你可以在你的基地里static_cast&lt;T*&gt;(this)

标签: c++ class-template


【解决方案1】:

你可能最好使用组合而不是继承,但我假设你有充分的理由更喜欢这里的继承。

困难在于,当基类构造函数运行时,this 指针只是指向基类实例的指针。派生实例甚至还没有退出。 (同样,在销毁时,对象的派生部分已经未初始化)。所以如果你调用一个虚方法,你会得到基类的实现而不是派生类的实现。在你的情况下,基类实现甚至不存在,所以你被卡住了。

您可以可能将基类的 this 指针转换为指向派生类的指针,但这并不能保证有效,并且可能涉及未定义的行为。

解决此问题的一种方法是存储指向基类型 (InstanceTracker *) 的指针,而不是指向派生类型的指针。那么你的getDerivedPtr 方法不需要是虚拟的,它可以在安全的情况下进行强制转换。

template <typename T>
class InstanceTracker {
    public:
        InstanceTracker() noexcept {
            allInstances_.push_back(this);
        }

        // other constructors elided for space

        virtual ~InstanceTracker() noexcept {
            std::erase(
                std::remove(allInstances_.begin(), allInstances_.end(), 
                            this),
                allInstances.end());
        }

        T* getDerivedPtr() {
            return static_cast<T*>(this);  // downcast
        }

    protected:
        // allInstances_ stores base class pointers
        static std::vector<InstanceTracker*> allInstances_;
};

注意事项:

  • 如果您使用 RTTI,运行时类型识别,您可以使用 dynamic_cast 而不是 static_cast。您不应该使用reinterpret_cast,因为编译器可能需要调整基指针作为转换的一部分。

  • 如果您将派生类型的实例创建为const,您可能会遇到问题。

【讨论】:

  • 1) 存储指向 InstanceTracker 的指针对我不起作用的原因是,当我遍历派生类中的静态向量时,我需要在这些指针上调用属于派生类的方法。 2) 当我将派生类的实例声明为 const 时,为什么会遇到问题?感谢您抽出时间来帮助我
猜你喜欢
  • 2022-01-05
  • 1970-01-01
  • 2019-03-02
  • 1970-01-01
  • 1970-01-01
  • 2020-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多