C ++通过类作用域访问实例的成员

时间:2012-03-06 15:12:20

标签: c++ linux segmentation-fault porting

我正在将应用程序从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;

}

1 个答案:

答案 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::*也不合格,因为PocketEntityClosedPocketEntity的虚拟基础。

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上使用的编译器允许将其作为扩展名,或者在这种情况下恰好“执行您想要的操作”。其他编译器似乎与强制无效的强制转换不同。

关于如何解决这个问题,恐怕我不知道。 (你确定你需要这么复杂的设计吗?)