首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >"pure virtual function call“仅调试时出错

"pure virtual function call“仅调试时出错
EN

Stack Overflow用户
提问于 2016-03-02 18:29:13
回答 3查看 522关注 0票数 0

下面的"Event“代码片段显示了”纯虚函数调用“错误。但是,正如标题中所提到的,只有在调试时部署时才会发生这种情况。让我好奇的是,为什么它在发布时可以完美地工作,为什么它甚至会崩溃(在调试时)。或者,您可以查看代码片段here

代码语言:javascript
复制
#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();
}
EN

回答 3

Stack Overflow用户

发布于 2016-03-02 18:57:43

我假设这个错误是用词不当,问题更可能与第二个委托所在的作用域有关。此外,在外部声明它更容易阅读。

传递堆栈上创建的对象,而不是通过引用传递堆,通常不是一个好主意。一旦项声明超出作用域,对象通常就会被遗忘。

票数 0
EN

Stack Overflow用户

发布于 2016-03-02 18:59:06

一般的问题是,您绑定到一个被销毁的临时对象,因此它有一个空的vtable,当然,当在属性更改时调用它时,它会生成一个纯虚拟调用。如果您为基类添加了一个dtor,这很容易观察到:

代码语言:javascript
复制
#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();
}

基本上你只是遇到了未定义的行为-对象在这两种情况下都会被销毁,但在发布时vtable不会被销毁,所以你可以通过。

票数 0
EN

Stack Overflow用户

发布于 2016-03-02 19:45:23

这一点:

代码语言:javascript
复制
propertyOwner.property1ChangedEvent.add(
    &PropertyChangedDelegate<PropertyObserver, int>(
    &propertyObserver, 
    &PropertyObserver::OnPropertyChanged)
);

您正在捕获指向临时对象PropertyChangedDelegate<PropertyObserver, int>的指针。一旦函数调用结束,指向这个对象的指针就会失效,临时对象也会被销毁。取消引用此指针是未定义的行为。

在你的程序中,内存所有权关系是至关重要的,你应该仔细考虑它们。您需要确保您的所有指针都比依赖它们的对象存活得更久,或者手动:

代码语言:javascript
复制
PropertyChangedDelegate<PropertyObserver, int> delegate2 = {
    &propertyObserver, 
    &PropertyObserver::OnPropertyChanged
};

propertyOwner.property1ChangedEvent.add(&delegate2);

或使用智能指针(std::unique_ptr<>std::shared_ptr<>)。

另一个错误:

符合C++11的compier不应允许您这样做:

代码语言:javascript
复制
std::list<PropertyChangedDelegateBase<TPropertyType>* const> observers_;

我在Visual Studio 2015中遇到的错误是:

C++标准禁止常量元素的容器,因为分配器的格式不正确。

请参阅:Does C++11 allow vector?

奖励:

你的C++风格看起来有点过时了。您可能希望尝试自动类型推导:

代码语言:javascript
复制
for(auto it = observers_.begin(); it != observers_.end(); ++it)
{
    (*it)->operator()(newValue);
}

或者,更好的是,ranged for循环:

代码语言:javascript
复制
for(auto observer : observers)
{
    observer(newValue);
}

您可能需要查看以下内容:

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35744554

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档