Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
3.8k views
in Technique[技术] by (71.8m points)

c++ - How to implement a class template that provides functionality to its derived classes to track their instances?

I am trying to create an abstract class template (InstanceTracker) that classes can inherit from if they need functionality to perform operations on all of their instances. The class holds a static vector of pointers to 'T', and every time an InstanceTracker constructor is run, I push back a new pointer to the vector. I do this through a purely virtual getDerivedPtr() method that returns 'T*', that every class that derives from InstanceTracker has to implement with return this;. You can probably already see what is wrong what this though. You can never call a purely virtual function from a base constructor - since it doesn't exist yet. How can I find a way around this problem for my InstanceTracker class? Here's the code for the class:

#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_;
};

If you want to try to run the code and see why it doesn't work at the moment, here's a simple class that inherits from InstanceTracker:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You'd probably be better off using composition rather than inheritance, but I'll assume you have have a good reason to prefer inheritance here.

The difficulty is that, when a base class constructor is run, the this pointer is a pointer to an instance of the base class only. The derived instance doesn't even exit yet. (Likewise, on destruction, the derived portion of the object has already been uninitialized). So if you call a virtual method, you'll get the base class implementation rather than the derived class's implementation. In your case, the base class implementation doesn't even exist, so you're stuck.

You can probably get away with casting the base class's this pointer to a pointer to the derived class, but that's not guaranteed to work and probably involves undefined behavior.

One way to solve this is to store pointers to the base type (InstanceTracker *) rather the pointers to the derived type. Then your getDerivedPtr method doesn't need to be virtual, and it can do the cast when it's safe.

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_;
};

Notes:

  • If you use RTTI, run-time type identification, you can use dynamic_cast instead of static_cast. You should not use a reinterpret_cast because the compiler might need to adjust the base pointer as part of the cast.

  • You're likely to run into problems if you create an instance of a derived type as const.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...