声明继承的模板化对象并将它们推回矢量 - 优雅的方式
Declare inherited templatized objects and push them back into vector - elegant way?
我正在尝试声明许多从Base
继承的模板化Derived<T>
对象,并将它们推回std::vector<Base*>
。
struct Base { ... };
template<typename T> struct Derived : Base { /* ctor(const string&) */ ... }
Derived<bool> online{"online"};
Derived<bool> official{"official"};
Derived<bool> noRotation{"no_rotation"};
Derived<bool> noBackground{"no_background"};
Derived<int> noSound{"no_sound"};
Derived<string> noMusic{"no_music"};
Derived<bool> blackAndWhite{"black_and_white"};
vector<Base*> configValues{&online,
&official,
&noRotation,
&noBackground,
&noSound,
&noMusic,
&blackAndWhite};
如您所见,代码很糟糕。有没有办法在不将vector
作为Derived<T>::Derived<T>(...)
构造函数中的const&
传递的情况下自动执行此操作?
通过自动化,我的意思是避免重复对象的名称。我想用我所有的Derived<T>
对象填充std::vector
,而不必手动列出它们。
(宏被接受,但希望有更好的解决方案(
因此,生命周期将成为您面临的问题之一,而如何在程序中使用此模式是对解决方案的另一个影响。
因此,假设您的派生<>实例不属于向量,那么您需要确保它们的寿命超过向量。有三种基本方法,在某些情况下可以结合起来。
第一个:创建一个存储和填充向量的类。如果复制了同一组派生<>类型或参数,这至少可以"删除"您的通用结构。然后,您可以为这些成员函数提供返回或填充向量。
第二种是使用 std::tuple。如果有许多参数列表变体,并且您希望一个实例存储所有这些派生实例,同时有办法创建通用例程(如填充向量(,则元组可能很有用:
typedef std::tuple<
Derived<bool>,
Derived<bool>,
Derived<bool>,
Derived<bool>,
Derived<bool>,
Derived<int>,
Derived<std::string>,
Derived<bool>
> atuple;
atuple t{
"online",
"official",
"no_rotation",
"no_background",
"no_sound",
"no_music",
"black_and_white"
};
const size_t size(std::tuple_size<atuple>::value);
/* or you could use std::array because the size is constant. */
std::vector<Base*>configValues;
configValues.reserve(size);
push(t,configValues);
push()
看起来像:
template<std::size_t I = 0, typename V, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
push(std::tuple<Tp...>& t, V&)
{ }
template<std::size_t I = 0, typename V, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
push(std::tuple<Tp...>& t, V& vec)
{
vec.push_back(&std::get<I>(t));
push<I + 1, V, Tp...>(t, vec);
}
(借用自元组迭代(。
如果您在程序的多个部分中没有遇到此问题,那么这些解决方案对您来说就没有那么有用了。
第三个 - 使用数组:
std::array<Derived<bool>,5> a{{{"online"},{"official"},{"no_rotation"},{"no_background"},{"black_and_white"}}};
Derived<int> noSound{"no_sound"};
Derived<string> noMusic{"no_music"};
vector<Base*> configValues{&noSound,&noMusic};
for (Derived<bool>& b:a) configValues.push_back(&b); // original order not retained
这里似乎有两个单独的问题...
出现的第一个问题是,您不想将常量std::string
传递给 Derived<T>
的构造函数。我能想到这样做的唯一原因是如果在构造Derived<T>
对象时需要修改字符串。如果您不需要修改字符串,我建议您像当前一样通过引用const
来获取它。
如果确实需要修改构造函数中的字符串,则可以更改参数以通过右值引用或值传递字符串。
Derived(std::string&& str) { /*...*/ } // Pass by r-value reference
Derived(std::string str) { /*...*/ } // Pass by value
两者都允许您在构造期间修改字符串。
至于你评论中的第二个问题...
要以注释中描述的方式填充向量,您可以使用统一初始化。唯一需要注意的是,您需要对添加到矢量的对象使用动态存储持续时间。
std::vector<Base*> configValues{
{
new Derived<bool>{"online"},
new Derived<bool>{"official"},
new Derived<bool>{"no_rotation"},
new Derived<bool>{"no_background"},
new Derived<int>{"no_sound"},
new Derived<std::string>{"no_music"},
new Derived<bool>{"black_and_white"}
}
};
我不确定您的目标是什么,因此您决定如何管理这些对象的生命周期取决于您。我强烈建议使用智能指针来管理它们的生命周期和所有权。例如,如果您需要共享所有权,则可以使用以下示例中的std::shared_ptr
。
std::vector<std::shared_ptr<Base>> configValues{
{
std::shared_ptr<Base>(new Derived<bool>{"online"}),
std::shared_ptr<Base>(new Derived<bool>{"official"}),
std::shared_ptr<Base>(new Derived<bool>{"no_rotation"}),
std::shared_ptr<Base>(new Derived<bool>{"no_background"}),
std::shared_ptr<Base>(new Derived<int>{"no_sound"}),
std::shared_ptr<Base>(new Derived<std::string>{"no_music"}),
std::shared_ptr<Base>(new Derived<bool>{"black_and_white"})
}
};
由于std::initializer_list
要求对象可复制,因此不能使用std::unique_ptr
来管理对象的生存期。
听起来你打算采用工厂模式类型的实现,有一个通用container
,所有派生对象都会添加到其中。
下面是一个帮助您入门的工作示例(ideone(:
#include <string>
#include <vector>
#include <memory>
#include <algorithm>
#include <iostream>
struct Base
{
typedef std::shared_ptr<Base> SharedPtr;
virtual ~Base(){}
virtual void print(){}
template<class T>
static SharedPtr makeDerived(const std::string& name);
static std::vector<SharedPtr> objs;
};
std::vector<Base::SharedPtr> Base::objs;
template<class T>
struct Derived : public Base
{
Derived(const std::string& name):name(name){}
void print(){std::cout<<name<<std::endl;}
std::string name;
};
template<class T>
Base::SharedPtr Base::makeDerived(const std::string& name)
{
SharedPtr p = std::make_shared<Derived<T> >(Derived<T>(name));
objs.push_back(p);
return p;
}
int main()
{
Base::makeDerived<bool>("online");
Base::makeDerived<int>("users");
std::for_each(Base::objs.begin(),Base::objs.end(),
[](Base::SharedPtr p){p->print();} );
}
shared_ptr
应该帮助内存管理。你可以使用静态容器或制作一个对象来容纳所有容器,没有太大区别。
感谢您的回答,对他们都投了赞成票。最后,我决定创建一个处理所有权和创建的"帮助程序"类。
来自我的GitHub SSVUtilsJson存储库:
class Manager
{
private:
ssvu::MemoryManager<Base> memoryManager;
// MemoryManager internally has a vector<Base*> of owned pointers
public:
template<typename T> Derived<T>& create() { return memoryManager.create<Derived<T>>(); }
// MemoryManager::create<T> creates and stores a 'new T*' and returns a reference to it
};
用法(来自我的GitHub SSVOpenHexagon repo(
Manager lvm;
auto& online (lvm.create<bool>());
auto& official (lvm.create<string>());
auto& noRotation (lvm.create<int>());
auto& noBackground (lvm.create<double>());
auto& noSound (lvm.create<char>());
auto& noMusic (lvm.create<void>());
for(Base* b : lvm.getItems()) { /* do something */ }
- 继承函数的重载解析
- 如何在c++中为模板函数实例创建快捷方式
- 继承期间显示未知行为的子类
- 头文件-继承c++
- 为什么在保护模式下继承升级不起作用
- 在c代码之间共享数据的最佳方式
- 在C++中将函数压缩为两种方式
- 以支持继承的方式将自身shared_ptr添加到对象构造函数中的向量中
- C++多重继承 - 相同的方法名称 - 我可以以某种方式删除其中一个吗?
- 升级到以多种方式为基类的C 类(不必使用虚拟继承)
- 我应该采取什么方式使用 qt:all 类继承自 QObject 或删除 新后手动删除
- 继承顺序C++以何种方式影响构造函数
- 在C++中定义虚拟继承的两种不同方式
- 我能以某种方式使用继承吗
- 声明继承的模板化对象并将它们推回矢量 - 优雅的方式
- Java和c++中重载方法的继承在基本方式上有所不同
- 你能把一个异常继承层次结构包装到另一个吗?——或者,另一种干净的处理方式
- 构造函数在私有继承中的工作方式
- 从qt中继承表单的最佳方式
- 使用私有继承时继承基构造函数的方式