2012-03-06 99 views
1

我正在將應用程序從Windows移植到Linux。 一個組件從文件讀取結構化數據。C++通過類範圍訪問實例的成員

樣品輸入: #10 = CLOSED_POCKET(2.0,地毯);

對於每個可能的實體,都會從類型定義中生成相應的C++類。 工廠根據實體的名稱(如CLOSED_POCKET)創建相應的對象。之後,這些屬性被一個接一個地讀取。因此,我們希望通過當前屬性的索引來分配C++類的成員。

代碼正常與Visual Studio 2010中的Windows編譯我移植的代碼到Linux 10.04(清醒山貓)和Eclipse CDT的靛藍與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; 

} 
+9

哦,我......... – 2012-03-06 15:16:12

回答

2
BaseAttribute ClosedPocketEntity::* ClosedPocketEntity::memberPtrArray[] = { 
     (BaseAttribute ClosedPocketEntity::*) &PocketEntity::depth, 
     (BaseAttribute ClosedPocketEntity::*) &ClosedPocketEntity::surfaceType 
}; 

你在這裏迫使第一指針轉換是無效的。從C++ 03§4.11/ 2 指針構件轉換

類型的「指針類型CV T,的B的構件」,其中B是一個類類型,右邊的值可以被轉換爲一個如果B是不可訪問的(第11章),不明確的(10.2)或虛擬(10.1)基本類型的「指向類型爲cv T的D的成員的指針」的D,需要這種 轉換的程序是不合格的。

(措辭在C++ 11,據我可以告訴不變。)

&PocketEntity::depthRealAttribute PocketEntity::*型的,所以即使是轉換爲RealAttribute ClosedPocketEntity::*將形成不良的,因爲PocketEntity是一個虛擬基地ClosedPocketEntity。如果刪除虛擬繼承

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上使用的編譯器允許將其作爲擴展,或者恰好在這種情況下「做你想做的事」。其他編譯器似乎與強制無效轉換有所不同。

至於如何解決這個問題,恐怕我不知道。 (你確定你需要這麼複雜的設計嗎?)

+0

謝謝你的回覆。我也嘗試了不同的其他鏈接場景,但似乎無法正確計算偏移量(正如我所希望的那樣)。由於這實際上是違法的C++,我重構了機制併爲每個實例創建了一個memberPtrArray,它可以正確使用gcc和msvc。 – FSaccilotto 2012-03-13 06:28:25