C++ 将指针转换为指针 (x**) -> (y**)
Posted
技术标签:
【中文标题】C++ 将指针转换为指针 (x**) -> (y**)【英文标题】:C++ casting pointer to pointers (x**) -> (y**) 【发布时间】:2014-12-04 22:09:34 【问题描述】:我对 C++ 还很陌生,因此我决定对指针的指针进行一些试验。我正在尝试创建一个系统,我可以在其中使用抽象类来定义许多不相关的类可以继承的接口(通常通过多重继承),这样我就可以将它们推到一些常见的辅助函数中。
我已经尽可能地简化了代码,所以如果列表有点长,我深表歉意......
//------------ HEADER
struct IClassData
virtual int Width() = 0;
virtual int Height() = 0;
;
class ClassA
public:
ClassA(int w, int h);
protected:
int _Width;
int _Height;
;
class Derived : public ClassA, public IClassData
public:
Derived();
Derived(int w, int h);
virtual int Width() override;
virtual int Height() override;
;
class Computron
public:
static int TotalWidth(IClassData** data, int size)
int curSum = 0;
for (int i = 0; i < size; i++)
curSum += data[i]->Width();
return curSum;
;
// -------------- CPP
ClassA::ClassA(int w, int h)
_Width= w;
_Height = h;
Derived::Derived() : ClassA(0,0) /* NONE */
Derived::Derived(int w, int h) : ClassA(w,h) /* NONE */
int Derived::Width() return _Width;
int Derived::Height() return _Height;
因此,在我的主程序中,我创建了许多派生类的实例,并且我想做一些转换,以便它可以像这样与 computron 函数一起工作:
// --------------- MAIN
...
const int MAX = 5;
Derived** data = new Derived*[MAX];
for (int i = 0; i < MAX; i++)
data[i] = new Derived(i, i*2);
// Cast an compute
int size = Computron::TotalWidth((IClassData**)data, MAX);
...
所以我的主要问题是: 1. 这是可以做的事情,还是我以后会在脚上开枪?我意识到我的类在这里非常简单,但如果它们变得非常复杂,会不会导致一些问题? 2. 另外,这样的演员阵容是否有可能由于某种原因在运行时无法正常工作? 3. 我最好只为这些类型的场景使用模板吗?
【问题讨论】:
"这是可以做的事情吗,还是我以后会在脚上开枪?"有什么特别的吗?您能否详细说明对您的设计的特定疑问,或者至少提供一个简洁的代码示例来重现您的问题? 如果你没有很好的理由,你不应该使用纯接口。你知道我们有模板吗?Derived** data = new Derived*[MAX]
[MAX]之前的星号是什么?
@JonathanWood 这分配了一个MAX
元素的数组,其中每个元素都是一个Derived *
(指向Derived
的指针)。这个分配的结果是一个Derived **
(pointer-to-pointer-to-Derived
)。
【参考方案1】:
这是可以做的事情,还是我以后会在脚上开枪?我意识到我的类在这里非常简单,但是如果它们变得非常复杂,会不会导致一些问题?
我在这里看不到任何会引起麻烦的东西一般,但我强烈建议使用标准库来避免以后出现内存管理问题。例如,我将使用以下数组而不是您的数组:
std::vector<std::unique_ptr<Derived>> data;
当data
超出范围时,向量将被销毁。这反过来会导致 unique_ptr 被销毁,它们会自动删除 Derived
对象。
然而,你只需要指针在你需要多态并且在同一个数组中存储不同类型的对象;也就是说,如果您要存储指向 IClassData
对象的指针,您可能更需要这个,因为您不确切知道在运行时数组中将出现什么派生类型。如所写,您可以简单地使用std::vector<Derived>
,因为您知道所有对象都是Derived
实例。
另外,这样的演员表是否有可能由于某种原因在运行时无法正常工作?
不要在 C++ 中使用 C 风格的强制转换。你应该在这里使用static_cast
,而it doesn't even work at compile time,因为它违反了类型系统。
Derived ** a = new Derived*[MAX];
Base ** b = static_cast<Base **>(a); // This line will not compile, because...
b[0] = new Base(); // Now a[0] is a Base rather than a Derived??
a[0]->some_derived_method(); // What should happen!?!?
为这些类型的场景使用模板会更好吗?
每种情况都不同,所以答案是确定的“也许”。
【讨论】:
请注意unique_ptr
需要 C++11 或更高版本。在早期版本中,只有 auto_ptr
,在 STL 容器中使用是不安全的(创建 unique_ptr
以替换 auto_ptr
的原因之一)。
谢谢,但我不是在寻找有关内存管理的建议。是的,如果我有一个只使用派生的情况,我会那样做。我在询问可能使用 IClassData 并因此可以发送到提到的实用程序函数的多种未知类型。
@A.R.实用函数很好,但实际上您必须从 IClassData*
的数组开始。您不能将指针指向不同的元素类型。
@A.R.因为您使用的是 C 风格的演员表,在这种情况下基本上相当于 reinterpret_cast
- 这是未定义的行为,并且按您的意图工作是(许多)可能的结果之一。在不同的编译器或同一编译器的不同版本上尝试它,它很可能在运行时崩溃。这就是为什么您从不在 C++ 中使用 C 风格的强制转换的原因; reinterpret_cast
是非常危险的,并且通过 C 风格的演员表,你可以不用尝试就可以得到这种行为。 (请参阅this article,了解 C 样式转换在 C++ 中的作用。)
它可能适用于您的情况,因为Derived
没有引入任何新的虚函数,因此 vtable 要么在内存中完全重叠,要么它们对于两个类来说只是等价的,所以它的行为当您使用指向错误的指针时正确。如果编译器做出了不同的决定,那么您的代码很可能会在运行时崩溃。底线:未定义的行为是不好的。不要调用它,即使它现在看起来可以工作——它以后可能不会工作。以上是关于C++ 将指针转换为指针 (x**) -> (y**)的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 C++ lambda 将成员函数指针转换为普通函数指针以用作回调