gpt4 book ai didi

C++通过类范围访问实例的成员

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:27:21 25 4
gpt4 key购买 nike

我正在将应用程序从 Windows 移植到 Linux。一个组件从文件中读取结构化数据。

示例输入:#10=CLOSED_POCKET(2.0, 地毯);

对于每个可能的实体,都会从类型定义中生成相应的 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++ 标准向下转换虚拟继承类是非法的(参见 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;

}

最佳答案

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

您在此处强制执行的第一个指针转换无效。来自 C++03 §4.11/2 指向成员转换的指​​针:

An rvalue of type “pointer to member of B of type cv T,” where B is a class type, can be converted to an rvalue of type “pointer to member of D of type cv T,” where D is a derived class (clause 10) of B. If B is an inaccessible (clause 11), ambiguous (10.2) or virtual (10.1) base class of D, a program that necessitates this conversion is ill-formed.

(据我所知,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 上使用的编译器要么允许将此作为​​扩展,要么恰好在这种情况下“做你想做的事”。其他编译器似乎以不同方式处理强制无效转换。

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

关于C++通过类范围访问实例的成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9586355/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com