下面的"Event“代码片段显示了”纯虚函数调用“错误。但是,正如标题中所提到的,只有在调试时部署时才会发生这种情况。让我好奇的是,为什么它在发布时可以完美地工作,为什么它甚至会崩溃(在调试时)。或者,您可以查看代码片段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();
}发布于 2016-03-02 18:57:43
我假设这个错误是用词不当,问题更可能与第二个委托所在的作用域有关。此外,在外部声明它更容易阅读。
传递堆栈上创建的对象,而不是通过引用传递堆,通常不是一个好主意。一旦项声明超出作用域,对象通常就会被遗忘。
发布于 2016-03-02 18:59:06
一般的问题是,您绑定到一个被销毁的临时对象,因此它有一个空的vtable,当然,当在属性更改时调用它时,它会生成一个纯虚拟调用。如果您为基类添加了一个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不会被销毁,所以你可以通过。
发布于 2016-03-02 19:45:23
这一点:
propertyOwner.property1ChangedEvent.add(
&PropertyChangedDelegate<PropertyObserver, int>(
&propertyObserver,
&PropertyObserver::OnPropertyChanged)
);您正在捕获指向临时对象PropertyChangedDelegate<PropertyObserver, int>的指针。一旦函数调用结束,指向这个对象的指针就会失效,临时对象也会被销毁。取消引用此指针是未定义的行为。
在你的程序中,内存所有权关系是至关重要的,你应该仔细考虑它们。您需要确保您的所有指针都比依赖它们的对象存活得更久,或者手动:
PropertyChangedDelegate<PropertyObserver, int> delegate2 = {
&propertyObserver,
&PropertyObserver::OnPropertyChanged
};
propertyOwner.property1ChangedEvent.add(&delegate2);或使用智能指针(std::unique_ptr<>、std::shared_ptr<>)。
另一个错误:
符合C++11的compier不应允许您这样做:
std::list<PropertyChangedDelegateBase<TPropertyType>* const> observers_;我在Visual Studio 2015中遇到的错误是:
C++标准禁止常量元素的容器,因为分配器的格式不正确。
奖励:
你的C++风格看起来有点过时了。您可能希望尝试自动类型推导:
for(auto it = observers_.begin(); it != observers_.end(); ++it)
{
(*it)->operator()(newValue);
}或者,更好的是,ranged for循环:
for(auto observer : observers)
{
observer(newValue);
}您可能需要查看以下内容:
https://stackoverflow.com/questions/35744554
复制相似问题