C++ &引用;“纯虚拟函数调用”;仅在调试时出错

C++ &引用;“纯虚拟函数调用”;仅在调试时出错,c++,events,observer-pattern,pure-virtual,C++,Events,Observer Pattern,Pure Virtual,下面的“事件”代码段显示了“纯虚拟函数调用”错误。但是,正如标题中提到的,只有在调试时部署时才会发生这种情况。让我好奇的是,为什么它在发布时能完美地工作,为什么它甚至会崩溃(在调试时)。 或者,您可以查看代码段 #包括 #包括 #包括 //使用基类解决如何将不同类型的对象放入集合中的问题 模板 结构属性ChangedDelegateBase { virtual~propertychangedelegatebase(){}; 虚空运算符()(const TPropertyType&t)=0; };

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

#包括
#包括
#包括
//使用基类解决如何将不同类型的对象放入集合中的问题
模板
结构属性ChangedDelegateBase
{
virtual~propertychangedelegatebase(){};
虚空运算符()(const TPropertyType&t)=0;
};
模板
结构PropertyChangedDelegate:PublicPropertyChangedDelegateBase
{
唐德勒·罗瓦纳*芬德勒·罗瓦纳;
typedef void(THandlerOwner::*TPropertyChangeHandler)(const TPropertyType&);
TPropertyChangeHandler\ux;
公众:
PropertyChangedElegate(THandlerOwner*pHandlerOwner,TPropertyChangeHandler处理程序):
pHandlerOwner(pHandlerOwner),handler(handler){}
void运算符()(常量TPropertyType&t)
{
(pHandlerOwner->*handler)(t);
}
};
模板
类PropertyChangedEvent
{
公众:
virtual~PropertyChangedEvent(){};
无效添加(PropertyChangedDelegateBase*const d)
{
std::list::const\u迭代器it=std::find(观察者\开始(),观察者\结束(),d);
if(it!=Observators_uz.end())
抛出std::runtime_错误(“观察者已注册”);
观察者向后推(d);
}
作废删除(PropertyChangedDelegateBase*const d)
{      
std::list::const\u迭代器it=std::find(观察者\开始(),观察者\结束(),d);
if(it!=Observators_uz.end())
移除(d);
}  
//通知
void运算符()
{
std::list::const_迭代器it=obsers_u.begin();
for(;it!=Observators_u.end();++it)
{
(*it)->operator()(newValue);
}
}
受保护的:
std::列出观察者;
};
类别属性所有者
{
int属性1;
浮动资产2;
公众:
财产变动财产变动;
财产变更财产变更;
PropertyOwner():
不动产1_0,
不动产2(0.0f)
{}  
int property1()常量{return property1_;}
无效属性1(整数n)
{
如果(属性1_!=n)
{
财产1=n;
属性1变化项(n);
}
}
float property2()常量{return property2_;}
无效属性2(浮动n)
{
如果(属性2_!=n)
{
财产2=n;
属性2变更开发(n);
}
}
};
结构属性观察服务器
{  
void OnPropertyChanged(常量int和newValue)
{

std::cout我认为错误是用词不当的,问题更可能与第二个委托所处的作用域有关。另外,在外部声明它更容易阅读


通过引用传递在堆栈上而不是堆上创建的对象通常是一个坏主意。一旦项声明超出范围,该对象通常会被忘记。

一般问题是,您绑定到一个临时对象,该临时对象被销毁,因此有一个空的
vtable
,当然它会生成一个纯vir在更改属性时调用实际调用。如果为基类添加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();
}
#包括
#包括
#包括

基本上,您只是遇到了未定义的行为-在这两种情况下对象都会被销毁,但在版本中,
vtable
不会被销毁,因此您可以通过。

这:

propertyOwner.property1ChangedEvent.add(
    &PropertyChangedDelegate<PropertyObserver, int>(
    &propertyObserver, 
    &PropertyObserver::OnPropertyChanged)
);
或者,更好的,为循环设置范围:

for(auto observer : observers)
{
    observer(newValue);
}
您可能想看看:


在发布时可以完美地工作,为什么它会崩溃(在调试时)
只是发布版本没有启用此运行时检查。不要认为它是完美的。@Drop-这不是关于运行时检查-纯粹的虚拟调用根本不会发生,因为
vtable
仍然存在。@RudolfsBundulis你为什么认为它“不存在”在调试版本中?@删除,因为我可以在调试器中看到:D@RudolfsBundulis通过运行时检查,我可以在
\u purecall()
中看到vtable和
abort()
调用。你说“在外部声明更容易阅读”是什么意思?另外,你能给我解释一下吗“通过引用传递在堆栈上而不是堆上创建的对象通常是个坏主意。一旦项声明超出范围,对象通常会被遗忘“?我有点迷路了。在将委托传递到将其注册为观察者的方法之前声明委托更容易阅读。虽然这是主观的,但给出一个名称比没有更有用。您想更深入地了解内存管理,特别是“堆栈与堆”示例显示了声明外部和“内部”,尽管我想确定发生了什么。尽管如此,我现在在事件类内部管理观察家。我发现这种方法更好,因为这些类应该包装在一个库中(我正在开发一个解析TMX文件的库,请参见mapreditor.org),让用户拥有一堆“外面"委托在他们的类中。以下是我所做的:你认为如何?我喜欢它的灵活性,而且它很好地使用了模板。但它是否需要那么灵活?让每个事件都有一个虚拟方法的类并继承它会更简单。我最近做了太多的iOS,他们的委托模式很容易被删除最后,谢谢。我不知道。原因是:这确实提供了很大的灵活性,这是我们考虑的,因为它是一个库。但是,我不喜欢C++中使用的匿名类型(模板)的实现(组织模板头)。.前面的方法正是你所说的:而不是在文章摘要中有几个事件
PropertyChangedDelegate<PropertyObserver, int> delegate2 = {
    &propertyObserver, 
    &PropertyObserver::OnPropertyChanged
};

propertyOwner.property1ChangedEvent.add(&delegate2);
std::list<PropertyChangedDelegateBase<TPropertyType>* const> observers_;
for(auto it = observers_.begin(); it != observers_.end(); ++it)
{
    (*it)->operator()(newValue);
}
for(auto observer : observers)
{
    observer(newValue);
}