简体   繁体   English

仅调试时出现“纯虚函数调用”错误

[英]“pure virtual function call” error on Debug ONLY

The following "Event" code snippet shows the "pure virtual function call" error. 以下“事件”代码段显示“纯虚拟函数调用”错误。 However, as mentioned in the title, it happens only when deploying on DEBUG. 但是,如标题中所述,仅在DEBUG上部署时才会发生。 What makes me curious is why it works flawlessly on RELEASE and why it does even crash (on DEBUG). 让我感到奇怪的是,为什么它在RELEASE上可以完美运行,为什么甚至崩溃(在DEBUG上)。 Alternatively, you can see the snippet here . 或者,您可以在此处查看代码段。

#include <list>
#include <iostream>
#include <algorithm>

// use base class to resolve the problem of how to put into collection objects of different types
template <typename TPropertyType>
struct PropertyChangedDelegateBase
{
    virtual ~PropertyChangedDelegateBase(){};
    virtual void operator()(const TPropertyType& t) = 0;
};

template <typename THandlerOwner, typename TPropertyType>
struct PropertyChangedDelegate : public PropertyChangedDelegateBase<TPropertyType>
{
    THandlerOwner* pHandlerOwner_;

    typedef void (THandlerOwner::*TPropertyChangeHandler)(const TPropertyType&);
    TPropertyChangeHandler handler_;

public:
    PropertyChangedDelegate(THandlerOwner* pHandlerOwner, TPropertyChangeHandler handler) :
      pHandlerOwner_(pHandlerOwner), handler_(handler){}

    void operator()(const TPropertyType& t)
    {
        (pHandlerOwner_->*handler_)(t);
    }
};

template<typename TPropertyType>
class PropertyChangedEvent
{
public:
    virtual ~PropertyChangedEvent(){};

    void add(PropertyChangedDelegateBase<TPropertyType>* const d)
    {
        std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d);
        if(it != observers_.end())
            throw std::runtime_error("Observer already registered");

        observers_.push_back(d);
    }


    void remove(PropertyChangedDelegateBase<TPropertyType>* const d)
    {      
        std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d);
        if(it != observers_.end())
            observers_.remove(d);
    }  

    // notify
    void operator()(const TPropertyType& newValue)
    {
        std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = observers_.begin();
        for(; it != observers_.end(); ++it)
        {
            (*it)->operator()(newValue);
        }
    }

protected:
    std::list<PropertyChangedDelegateBase<TPropertyType>* const> observers_;
};

class PropertyOwner
{
    int property1_;
    float property2_;

public:
    PropertyChangedEvent<int> property1ChangedEvent;
    PropertyChangedEvent<float> property2ChangedEvent;

    PropertyOwner() :
        property1_(0),
        property2_(0.0f)
    {}  

    int property1() const {return property1_;}
    void property1(int n)
    {
        if(property1_ != n)
        {
            property1_ = n;
            property1ChangedEvent(n);
        }
    }

    float property2() const {return property2_;}
    void property2(float n)
    {
        if(property2_ != n)
        {
            property2_ = n;
            property2ChangedEvent(n);
        }
    }
};

struct PropertyObserver
{  
    void OnPropertyChanged(const int& newValue)
    {
        std::cout << "PropertyObserver::OnPropertyChanged() -> new value is: " << newValue << std::endl;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    PropertyOwner propertyOwner;
    PropertyObserver propertyObserver;

    // register observers
    PropertyChangedDelegate<PropertyObserver, int> delegate(&propertyObserver, &PropertyObserver::OnPropertyChanged);

    propertyOwner.property1ChangedEvent.add(&delegate); // Ok!
    propertyOwner.property1ChangedEvent.add(&PropertyChangedDelegate<PropertyObserver, int>(&propertyObserver, &PropertyObserver::OnPropertyChanged)); // Error: Virtual pure function call (Debug only)
    propertyOwner.property1(1);

    return getchar();
}

I would assume that the error is misnomer and that the problem is more likely to do with the scope that the second delegate lives. 我会认为该错误使用不当,并且该问题更有可能与第二个代表所处的范围有关。 Plus declaring it outside is easier to read. 加上在外面声明它更容易阅读。

Passing around an object created on the stack rather than the heap by reference is usually a bad idea. 传递通过堆栈而不是通过引用创建的对象通常是一个坏主意。 Once the item declaration is out of scope the object is usually forgotten about. 项目声明超出范围后,通常会忘记该对象。

The general issue is that you are binding to a temporary that gets destroyed and thus has an empty vtable and of course it generates a pure virtual call when invoked on the change of the property. 普遍的问题是,您绑定到一个临时文件,该临时文件将被销毁,因此具有空的vtable并且当对属性的更改进行调用时,它当然会生成纯虚拟调用。 If you add a dtor for the base class this is quite easy to observe: 如果为基类添加一个dtor,则很容易观察到:

#include <list>
#include <iostream>
#include <algorithm>

// use base class to resolve the problem of how to put into collection objects of different types
template <typename TPropertyType>
struct PropertyChangedDelegateBase
{
    virtual ~PropertyChangedDelegateBase(){};
    virtual void operator()(const TPropertyType& t) = 0;
};

template <typename THandlerOwner, typename TPropertyType>
struct PropertyChangedDelegate : public PropertyChangedDelegateBase<TPropertyType>
{
    THandlerOwner* pHandlerOwner_;

    typedef void (THandlerOwner::*TPropertyChangeHandler)(const TPropertyType&);
    TPropertyChangeHandler handler_;

public:
    PropertyChangedDelegate(THandlerOwner* pHandlerOwner, TPropertyChangeHandler handler) :
        pHandlerOwner_(pHandlerOwner), handler_(handler)
    {
        std::cout << "0x" << std::hex << this << " created!" << std::endl;
    }

    void operator()(const TPropertyType& t)
    {
        (pHandlerOwner_->*handler_)(t);
    }

    ~PropertyChangedDelegate()
    {
        std::cout << "0x" << std::hex << this << " destroyed!" << std::endl;
    }
};

template<typename TPropertyType>
class PropertyChangedEvent
{
public:
    virtual ~PropertyChangedEvent(){};

    void add(PropertyChangedDelegateBase<TPropertyType>* const d)
    {
        std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d);
        if (it != observers_.end())
            throw std::runtime_error("Observer already registered");

        observers_.push_back(d);
    }


    void remove(PropertyChangedDelegateBase<TPropertyType>* const d)
    {
        std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d);
        if (it != observers_.end())
            observers_.remove(d);
    }

    // notify
    void operator()(const TPropertyType& newValue)
    {
        std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = observers_.begin();
        for (; it != observers_.end(); ++it)
        {
            std::cout << "Invoking 0x" << std::hex << *it << std::endl;
            (*it)->operator()(newValue);
        }
    }

protected:
    std::list<PropertyChangedDelegateBase<TPropertyType>* const> observers_;
};

class PropertyOwner
{
    int property1_;
    float property2_;

public:
    PropertyChangedEvent<int> property1ChangedEvent;
    PropertyChangedEvent<float> property2ChangedEvent;

    PropertyOwner() :
        property1_(0),
        property2_(0.0f)
    {}

    int property1() const { return property1_; }
    void property1(int n)
    {
        if (property1_ != n)
        {
            property1_ = n;
            property1ChangedEvent(n);
        }
    }

    float property2() const { return property2_; }
    void property2(float n)
    {
        if (property2_ != n)
        {
            property2_ = n;
            property2ChangedEvent(n);
        }
    }
};

struct PropertyObserver
{
    void OnPropertyChanged(const int& newValue)
    {
        std::cout << "PropertyObserver::OnPropertyChanged() -> new value is: " << newValue << std::endl;
    }
};

int main(int argc, char* argv[])
{
    PropertyOwner propertyOwner;
    PropertyObserver propertyObserver;

    // register observers
    PropertyChangedDelegate<PropertyObserver, int> delegate(&propertyObserver, &PropertyObserver::OnPropertyChanged);

    propertyOwner.property1ChangedEvent.add(&delegate); // Ok!
    propertyOwner.property1ChangedEvent.add(&PropertyChangedDelegate<PropertyObserver, int>(&propertyObserver, &PropertyObserver::OnPropertyChanged)); // Error: Virtual pure function call (Debug only)
    propertyOwner.property1(1);

    return getchar();
}

崩溃崩溃

Basically you are just running into undefined behavior - the object is destroyed in both cases, but in Release the vtable is not destroyed so you get by. 基本上,您只是遇到未定义的行为-两种情况下对象都被破坏,但是在Release中, vtable没有被破坏,所以您可以通过。

This: 这个:

propertyOwner.property1ChangedEvent.add(
    &PropertyChangedDelegate<PropertyObserver, int>(
    &propertyObserver, 
    &PropertyObserver::OnPropertyChanged)
);

You are capturing a pointer to a temporary object PropertyChangedDelegate<PropertyObserver, int> . 您正在捕获一个指向临时对象PropertyChangedDelegate<PropertyObserver, int>的指针。 Pointer to this object becomes invalid as soon as function call is over and temporary is destroyed. 一旦函数调用结束并且指向临时对象的指针就指向该对象的指针无效。 Dereferencing this pointer is undefined behavior. 取消引用此指针是未定义的行为。

In your program, memory ownership relations are critical and you should think them through carefully. 在您的程序中,内存所有权关系至关重要,您应该仔细考虑它们。 You need to ensure that all your pointers outlive objects that rely on them, either manually: 您需要手动确保所有指针都超出依赖它们的对象:

PropertyChangedDelegate<PropertyObserver, int> delegate2 = {
    &propertyObserver, 
    &PropertyObserver::OnPropertyChanged
};

propertyOwner.property1ChangedEvent.add(&delegate2);

or by using smart pointers ( std::unique_ptr<> , std::shared_ptr<> ). 或使用智能指针( std::unique_ptr<>std::shared_ptr<> )。

Another bug: 另一个错误:

C++11 compliant compier should not allow you doing this: 符合C ++ 11的编译器不应允许您这样做:

std::list<PropertyChangedDelegateBase<TPropertyType>* const> observers_;

The error I got with Visual Studio 2015 is: 我在Visual Studio 2015中遇到的错误是:

The C++ Standard forbids containers of const elements because allocator is ill-formed.` C ++标准禁止容器const元素,因为分配器的格式不正确。

See: Does C++11 allow vector<const T> ? 请参阅: C ++ 11是否允许vector<const T>

Bonus: 奖金:

Your C++ style looks quite a bit obsolete. 您的C ++样式看起来已经过时了。 You might want to try automatic type deduction: 您可能要尝试自动类型推断:

for(auto it = observers_.begin(); it != observers_.end(); ++it)
{
    (*it)->operator()(newValue);
}

or, better, ranged for loops: 或者更好的是,范围为循环:

for(auto observer : observers)
{
    observer(newValue);
}

You might want to take a look to: 您可能要看一下:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM