提问人:Nathan29006781 提问时间:12/13/2022 最后编辑:Nathan29006781 更新时间:12/15/2022 访问量:166
从具有已删除析构函数的类派生?
Deriving from a class with a deleted destructor?
问:
我正在使用 CRTP 创建一个计数器类,类似于对象计数器
此外,从此计数器派生的类也不应是可破坏的。
它看起来像这样
template <typename DERIVED_CLASS, std::size_t size = 0>
class Counter{
private:
~Counter() = delete;
protected:
std::vector<DERIVED_CLASS*> created;
Counter(){}
public:
//More stuff later
};
class A : public Counter<A>{
public:
A(){std::cout << "A created" << std::endl;}
//More stuff later
};
A a;
int main(){
/* All should be invalid
A b;
A c{a};
A* pA = new A;
*/
return 0;
}
创建应该是创建/删除这些类型的对象的唯一允许用法。它们不应该是可复制的,并且应该在整个程序生命周期内持续存在,因此不应该被破坏。a
但是,我收到以下错误和诊断消息
base class 'Counter<A>' has private destructor
destructor of 'A' is implicitly deleted because base class 'Counter<A>' has a deleted destructor
~Counter' has been explicitly marked deleted here
attempt to use a deleted function
编辑:
只是澄清一些事情。
当然,该对象将在程序退出时被销毁。这是期望的行为。它不应该被用户或在程序生命周期内破坏。
其次,可以创建多个对象。这不是一次创造的事情。许多可以创建,但不能复制或删除。
编辑:@joerbrech评论的代码
template <typename DERIVED_CLASS, std::size_t size = 0>
class Counter{
private:
Counter(Counter const &) = delete;
Counter& operator=(Counter const &) = delete;
protected:
~Counter(){}
Counter(){}
public:
template <typename... DERIVED_ARGS>
static DERIVED_CLASS& create(DERIVED_ARGS... args){
DERIVED_CLASS* pDerived = new DERIVED_CLASS(args...);
//Save the pointer into a static collection
return *pDerived;
}
//More stuff later
};
class A: public Counter<A>{
using Base = Counter<A>;
friend Base;
A(int x){}
//More stuff later
};
A& a = A::create(1);
int main(){
A& b = A::create(2); //Works (bad)
// A c(3); //Fails (good)
// A* pA = new A(4); //Fails (good)
(void)b; //To silence unused variable warning
return 0;
}
答:
您无法删除析构函数。如果希望变量在程序结束前不被销毁,则应将其声明为 。如果你的类只意味着每个都用作全局或静态实例,我会记录以下内容:如果你的库的用户在没有 static 关键字的情况下使用它,他们就会错误地使用你的库。static
此外,请注意,如果目标是让对象计数器工作,则不必确保在程序结束之前不会销毁派生类。由于数据成员 和 在链接中的示例中是静态的,因此这些变量将在派生类的第一次实例化之前开始存在,并且不早于程序结束时停止存在。https://godbolt.org/z/14zMhv993objects_created
objects_alive
如果要实现保护措施,使得无法将派生类用作非静态实例,我们可以从单例模式中复制。让我们暂时忘记,您希望允许类的多个实例并实现单例模式。
#include <iostream>
#include <cassert>
template <typename DERIVED_CLASS, std::size_t size = 0>
class Counter{
protected:
~Counter(){ std::cout << "Counter dtor\n"; }
Counter(){ std::cout << "Counter ctor\n"; }
public:
template <typename... DERIVED_ARGS>
static DERIVED_CLASS& create(DERIVED_ARGS... args){
static DERIVED_CLASS derived{args...};
return derived;
}
//Counter is non-copyable
Counter(Counter const &) = delete;
Counter& operator=(Counter const &) = delete;
//More stuff later
};
class A: public Counter<A>{
using Base = Counter<A>;
friend Base;
A(int x){}
//More stuff later
};
现在每个派生类,例如 永远只能有一个实例。内部静态变量 in 只会在第一次调用该函数时实例化一次。A
derived
Counter::create
另请注意,析构函数在程序退出时被调用:
int main(){
{
A& b = A::create(2);
// no new instance will be created at a second call
assert(&b == &A::create(42));
}
std::cout << "dtor will be called after this\n";
return 0;
}
输出:
Counter ctor
dtor will be called after this
Counter dtor
https://godbolt.org/z/xezKhx1fE
如果需要多个实例,则可以在 中创建一个静态集合,并在每次函数调用时将该集合放置/插入到集合中。您必须使用一个集合,以确保对元素的引用保持有效。例如,这是正确的,但对于 也是如此,因为具有连续的数据存储并且可能会重新分配。create
std::unordered_map
std::vector
std::vector
#include <unordered_map>
#include <memory>
#include <iostream>
#include <cassert>
template <typename DERIVED_CLASS, std::size_t size = 0>
class Counter{
protected:
Counter(){ }
public:
using Map = std::unordered_map<int, DERIVED_CLASS>;
static Map& get_map() {
static Map map;
return map;
}
template <typename... DERIVED_ARGS>
static DERIVED_CLASS& create(DERIVED_ARGS&&... args){
Map& map = get_map();
static int idx = 0;
map.insert(
std::make_pair(
idx,
DERIVED_CLASS{std::forward<DERIVED_ARGS>(args)...}
)
);
return map.at(idx++);
}
virtual ~Counter(){ }
//Counter is non-copyable
Counter(Counter const &) = delete;
Counter& operator=(Counter const &) = delete;
// it must be moveable though to insert into map
Counter(Counter&&) = default;
Counter& operator=(Counter&&) = default;
//More stuff later
};
class A: public Counter<A>{
using Base = Counter<A>;
friend Base;
A(int x){}
//More stuff later
};
int main(){
A& a = A::create(2);
A& b = A::create(42);
assert(&a == &A::get_map().at(0));
assert(&b == &A::get_map().at(1));
assert(&a != &b);
return 0;
}
https://godbolt.org/z/qTPdbvsoh
实例的析构函数在静态映射的析构函数调用中调用。当程序退出时,静态映射将被破坏。如果你想确保没有人修改你的静态地图,你可以设为私有。Counter
get_map
附录:当然,您可以在堆上创建 或 实例,但最好smart_pointers: 这保证了内存在程序退出时操作系统应该释放和不需要清理时释放内存。A
Counter
下一个:检测已删除的功能
评论
main
a
a
A
private
deleted
public
delete
Counter
private