且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

对基类和派生类使用静态容器

更新时间:2023-11-11 18:43:40

少数:使用托管指针。这是您的代码的修改版本基于 unique_ptr

  A {
// ...
static std :: unordered_map< std :: string,std :: unique_ptr< A>列表;

// ...
template<类T,类... Args>
static
//通过返回类型只允许基于A的对象T
typename std :: enable_if< std :: is_base_of< A,T> :: value> :: type
place(std :: string _n,Args& ... args)
{
list.emplace(_n,std :: make_unique< T>(std :: forward& (args)...));
}
// ...
};

std :: unordered_map< std :: string,std :: unique_ptr< A>> A :: list = {};

class B:public A {

// ...
//在此处更正警告:基础应在成员之前构建
B std :: string _n):A(_n),name(_n){}
// ...
};

int main(){
A :: init();
A :: place< B>(b1,b1);
A :: place< A>(a1,a1);
//可能,第二个参数现在是多余的
//可以删除它并调用到位
// std :: make_unique< T>(_ n,std :: forward& ;(args)...)
return 0;
}

我修改使用 make_unique 这是C ++ 14,你可以使用 new T {std :: forward< Args> (args)...} 。新的 place 函数是一个可变参数模板遵循 make_unique 原型。



如Adam Martin的评论所示, enable_if 可用于限制类型 T 到继承 A 的对象。



Jarod42,则应使用转发参考来尊重调用 place 。



我使用 emplace 而不是 operator [] 复制或移动操作。



另一个选择是使用 shared_ptr 。实施方式类似。


I'm trying to build a [base+derived] class's object container(std::map to be accurate) which would be a static variable, embedded inside Base class so that every other object either derived from it, or from it's child classes would've only 1 list at most. Also if no object is created, I still would've access to that list through other friendly(static) functions.

Here's the code -

#include <iostream>
#include <string>
#include <unordered_map>

class A{

    std::string id;
    static std::unordered_map<std::string, A*> list;

  public:

    A(std::string _i): id(_i){}
    static void init(){
        // doSomeProcessing or let's just call clear() for now
        list.clear();
    }
    static void place(std::string _n, A* a){
        list[_n] = a;
    }
    virtual void doSomething(){std::cout << "DoSomethingBase\n";}
};

std::unordered_map<std::string, A*> A::list = {};

class B : public A{

    std::string name;

  public:

    B(std::string _n):name(_n), A(_n){}
    void doSomething(){std::cout << "DoSomethingDerived\n";}
};


int main() {
    A::init();
    A::place("b1", new B("b1"));
    A::place("a1", new A("a1"));
    return 0;
}

The code compiles successfully however, there are two things at top of my mind, which I might be doing wrong, one major and another minor problem.

Major - Pointers as values to std::map? I'm sure I've totally not grasped them yet, but I'm studying hard. I know they aren't being deleted here, but how should I do it? In the destructor? or let the program finish.....(bad, right?)

Minor - Using static members? I've heard from folks that static variables are bad for some unknown reasons and shouldn't be used unless necessarily required. They were even removed from some early version of cpp. Is that right? But what would be my options then... creating it in main().

Any other mistake I might be doing here.


Edit - I'm supplying a sample scenario so to clear my intentions with following example.

Let's say I've an employee class and some more specific jobs eg editor, writer, developer that are still derived from employees. However there might exist instance of base class employee too, since there might be employees who aren't cateogrised yet, but will be soon.

Let's say this is an requirement, although I'm open to changes. I'm using that static std::map so that only one instance of it is required which is uniform across all base + derived classes. I'm not so familiar with singleton pattern and such but I'll happily learn it in future.

Major: use managed pointers. Here is a modified version of your code based on unique_ptr:

class A{
    // ...
    static std::unordered_map<std::string, std::unique_ptr<A>> list;

    // ...
    template< class T, class... Args >
    static
    // enable only A-based objects for T via the return type
    typename std::enable_if<std::is_base_of<A, T>::value>::type
    place(std::string _n, Args&&... args)
    {
        list.emplace(_n, std::make_unique<T>(std::forward<Args>(args)...));
    }
    // ...
};

std::unordered_map<std::string, std::unique_ptr<A>> A::list = {};

class B : public A{

    // ...
    // corrected a warning here: bases should be constructed before members
    B(std::string _n):A(_n), name(_n){}
    // ...
};

int main() {
    A::init();
    A::place<B>("b1", "b1");
    A::place<A>("a1", "a1");
        // maybe, the second argument is now redundant
        // you can remove it and call in place
        // std::make_unique<T>(_n, std::forward<Args>(args)...)
    return 0;
}

I modified place to make it care of the allocation -- using make_unique which is C++14, you can use new T{std::forward<Args>(args)...} instead. The new place function is a variadic template to follow make_unique prototype.

As suggested in the comments by Adam Martin, enable_if can be used to restrict the type T to objects inheriting A.

As edited by Jarod42, you should use forward references to respect the value category passed when calling place.

I used emplace instead of operator[] to avoid unnecessary copy or move operations.

An other option is to use shared_ptr if you need to make a copy of the pointer. The implementation would be similar.