我正在将应用程序从Windows移植到Linux。 一个组件从文件中读取结构化数据。
示例输入: #10 = CLOSED_POCKET(2.0,CARPET);
对于每个可能的实体,从类型定义生成相应的c ++类。 工厂根据实体的名称(即CLOSED_POCKET)创建相应的对象。之后,一个接一个地读取属性。因此,我们希望通过当前属性的索引来分配c ++类的成员。
代码在使用Visual Studio 2010编译的Windows上正常工作。我将代码移植到Linux 10.04(Lucid Lynx)并使用Eclipse CDT Indigo中的gcc 4.4.6成功编译。
Linux上的问题: 当我访问属性的方法时,调试器有时会跳转到错误的函数(当应该调用子类的函数时,函数偏移量不正确)会导致分段错误。
我做了一个最小的例子,它也导致了一个分段错误(见下文)。
我的问题是:当Windows能够成功运行时,我需要做什么,在使用GCC的Linux下运行它?
我知道根据c ++标准,Downcasting虚拟继承类是非法的(参见Downcast in a diamond hierarchy ),但也许存在另一种通过类范围访问实例成员的解决方案。 由于ISO标准给出的实体结构,因此需要虚拟继承。
我还想为每个实例提供一个访问数组(MemberPtrArray),但是读取了大约80'000个实体,对类范围的访问会更好。
/*
* MemberPointerTest.h
*/
#ifndef MAINTEST_H_
#define MAINTEST_H_
#include <string>
class BaseAttribute{
public:
virtual void SetReal(double value);
virtual void SetSelectName(std::string selectName);
};
class RealAttribute : public BaseAttribute{
public:
double value;
virtual void SetReal(double value);
};
class SelectAttribute: public BaseAttribute{
public:
std::string selectName;
virtual void SetSelectName(std::string selectName);
};
class BaseEntity{
public:
BaseAttribute id;
virtual ~BaseEntity(){}
};
class PocketEntity : virtual public BaseEntity{
public:
RealAttribute depth;
};
class ClosedPocketEntity : virtual public PocketEntity{
public:
SelectAttribute surfaceType;
static BaseAttribute ClosedPocketEntity::* memberPtrArray[3];
BaseAttribute* GetMember(unsigned int index);
};
#endif
/*
* MemberPointerTest.cpp
*/
#include "MemberPointerTest.h"
void BaseAttribute::SetReal(double value){
}
void BaseAttribute::SetSelectName(std::string selectName){
}
void RealAttribute::SetReal(double value){
this->value = value;
}
void SelectAttribute::SetSelectName(std::string selectName){
this->selectName = selectName;
}
BaseAttribute ClosedPocketEntity::* ClosedPocketEntity::memberPtrArray[] = {
(BaseAttribute ClosedPocketEntity::*) &PocketEntity::depth,
(BaseAttribute ClosedPocketEntity::*) &ClosedPocketEntity::surfaceType
};
/* Tried the following alternatives:
* &PocketEntity::depth, // cannot convert ‘RealAttribute PocketEntity::*’ to ‘BaseAttribute ClosedPocketEntity::*’ in initialization
* (RealAttribute ClosedPocketEntity::*) &ClosedPocketEntity::depth, // invalid conversion from ‘RealAttribute ClosedPocketEntity::*’ to ‘BaseAttribute ClosedPocketEntity::*’
*/
BaseAttribute* ClosedPocketEntity::GetMember(unsigned int index){
return &(this->*memberPtrArray[index]);
}
int main(){
ClosedPocketEntity cpEntity;
// Case 1: Calls SetReal of BaseAttribute
BaseAttribute* depthPtr = cpEntity.GetMember(0);
depthPtr->SetReal(3.0);
// Case 2: Produces Segmentation fault
RealAttribute* depthPtr2 = dynamic_cast<RealAttribute*>(cpEntity.GetMember(0));
depthPtr2->SetReal(2.0); // SIGSEGV
return 0;
}
答案 0 :(得分:2)
BaseAttribute ClosedPocketEntity::* ClosedPocketEntity::memberPtrArray[] = {
(BaseAttribute ClosedPocketEntity::*) &PocketEntity::depth,
(BaseAttribute ClosedPocketEntity::*) &ClosedPocketEntity::surfaceType
};
您在此处强制执行的第一个指针转换无效。从C ++03§4.11/ 2 指向成员转换的指针:
类型“指向cv T类型B的成员的指针”的rvalue,其中B是类类型,可以转换为类型为“指向cv T类型的D的成员的指针”的右值,其中D是a B的派生类(第10节)如果B是不可访问的(第11条),D的模糊(10.2)或虚拟(10.1)基类,则需要这样的程序 转换是不正确的。
(据我所知,C ++ 11中的措辞没有改变。)
&PocketEntity::depth
的类型为RealAttribute PocketEntity::*
,因此即使转换为RealAttribute ClosedPocketEntity::*
也不合格,因为PocketEntity
是ClosedPocketEntity
的虚拟基础。
clang++
有以下有用的错误消息:
error: conversion from pointer to member of class 'PocketEntity'
to pointer to member of class 'ClosedPocketEntity'
via virtual base 'PocketEntity' is not allowed
如果删除了虚拟继承,根据GCC和clang,转换仍然无效:
error: cannot initialize an array element of type
'BaseAttribute ClosedPocketEntity::*'
with an rvalue of type
'RealAttribute PocketEntity::*'
我在标准的那一部分中没有看到任何内容可以允许这种转换(但请注意我在这里不够深入,并且很可能在精彩的C ++转换规则中遗漏了一些内容。)
您在Windows上使用的编译器允许将其作为扩展名,或者在这种情况下恰好“执行您想要的操作”。其他编译器似乎与强制无效的强制转换不同。
关于如何解决这个问题,恐怕我不知道。 (你确定你需要这么复杂的设计吗?)