我有以下代码(来自Koening& Moo Accelerated C ++ 第255页),它定义了一个通用句柄类Handle
。 Handle
用于管理对象的内存:
#include <iostream>
#include <stdexcept>
///Handle
template <class T>
class Handle
{
public:
Handle() : p(0) {}
Handle &operator=(const Handle &);
T *operator->() const;
~Handle() { delete p; }
Handle(T *t) : p(t) {}
private:
T *p;
};
template <class T>
Handle<T> &Handle<T>::operator=(const Handle &rhs)
{
if (&rhs != this)
{
delete p;
p = rhs.p ? rhs.p->clone() : 0;
}
return *this;
};
template <class T>
T *Handle<T>::operator->() const
{
if (p)
return p;
throw std::runtime_error("error");
};
class test_classA
{
friend class Handle<test_classA>;
private:
virtual test_classA *clone() const { return new test_classA; }
public:
virtual void run() { std::cout << "HiA"; }
};
class test_classB : public test_classA
{
private:
virtual test_classB *clone() const { return new test_classB; }
public:
virtual void run() { std::cout << "HiB"; }
};
int main()
{
Handle<test_classA> h;
h = new test_classA;
h->run();
return 0;
}
当我使用g++ -o main main.cpp -Wall
编译时,我收到警告:
warning: deleting object of polymorphic class type ‘test_classA’ which has non-virtual destructor might cause undefined behaviour [-Wdelete-non-virtual-dtor]
~Handle() { delete p; }
我不太明白这个警告。 handle类自动删除析构函数中的指针*p
,无论其类型如何,那么潜在的陷阱在哪里?
答案 0 :(得分:1)
你的警告说明了一切。您的类admin.StackedInline
是多态的,但析构函数是非虚拟的。当基类没有虚析构函数时,通过指向基类的指针删除派生类的对象是未定义的行为。
在您的具体示例中,没有未定义的行为,因为您没有派生类的对象,但编译器可能无法确定它,因此无论如何都会向您发出警告。
答案 1 :(得分:1)
问题是,如果处理的对象是模板instanciation类型的子类,则会发生错误的删除。
在您的情况下,如果您的Handle<test_classA> h;
将处理test_classB
类型的对象,则会发生...
答案 2 :(得分:1)
句柄HttpServletRequest
的类型为@Override
public void configure(HttpSecurity http) {
http.authorizeRequests()
.antMatchers("/", "/signup").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/home", true)
.permitAll();
}
,因此它会存储指向h
的指针并调用Handle<test_classA>
的析构函数。但是,您可以在句柄中存储指向test_classA
的指针,在这种情况下test_classA
析构函数不会被调用,因为test_classB
析构函数不是虚拟的:
test_classB
另请注意,此test_classA
类的名称选择不当,它本质上是一种智能指针类。
答案 3 :(得分:1)
在C ++中,如果你有一个基类(这里,test_classA
),其中有一些派生自它的类(这里是test_classB
),你必须小心删除test_classA
类型的指针。 1}}如果这些指针实际上可能指向test_classB
类型的对象。请注意,您在此处编写的代码中正是这样做的。
如果您执行此类操作,则需要为基类(test_classA
)提供虚拟析构函数,如下所示:
class test_classA {
public:
virtual ~test_classA() = default;
// ...
};
这样,当C ++尝试删除类型为test_classA
的指针时,它知道有问题的指针实际上可能不会指向test_classA
对象,并且会正确调用正确的析构函数。
顺便提一下,这个问题完全独立于你的包装类型。您可以通过编写
来解决同样的问题test_classA* ptr = new test_classB;
delete ptr; // <--- Warning! Not good unless you have a virtual dtor.
答案 4 :(得分:0)
警告是由于您的基类的析构函数不是虚拟的。如果你想以多态方式使用你的类(例如,创建一个带有基类指针的向量,其中指向的对象是派生类),你将会有未定义的行为。
另外需要提及的是,您将Handle类声明为类test_classA的朋友,以便获得对clone功能的访问权限。请注意,friend关键字不可传递,因此在派生类中,Handle类无法访问克隆函数。
最后你的克隆功能对我来说不太清楚。我们来看看主要功能:
Handle<test_classA> h;
h = new test_classA
Handle(T *t) : p(t) {}
。然后,这将在您的operator =函数中使用,您可以在其中检查rhs.p是否为NULL。因为它不是null你将调用克隆函数(你写的等价于(rhs.p) - &gt;克隆所以它不是Handle类'operator-&gt;它被调用但指针p)将创建一个在HEAP再次记忆。我认为这不是你想要的。