C++类实例化是否可以在运行时更改其大小
Can a C++ class instantiation change its size during runtime
我这里有一个非常特殊的情况......我继承了一些旧的C++代码(pre-C++11),它又长又复杂,其中一半是用C编写的,另一半是用C-with-class的心态编写的(即:具有更多数据成员的类,没有太多的类方法,直接操作数据成员......当然,这个项目需要一些重构,这就是我现在正在做的事情)。但是代码暴露了一些我觉得令人费解的问题。
首先,让我们采取以下非常简单(无错误)的情况:
#include <iostream>
static int c = 0;
struct Bar
{
Bar() : base_id(++c) { std::cout << "Bar "<< base_id << std::endl;}
int base_id;
};
struct Foo : public Bar
{
Foo() : x(c) { std::cout << "Foo "<< x << std::endl;}
int x;
};
int main()
{
Bar* b = new Foo[200];
Foo *p;
for(p = (Foo*)b; p - (Foo*)b < 200; p ++ )
{
std::cout << p->base_id << " " << ((Foo*)p)->x
<< " p-b=" << (unsigned)(p-(Foo*)b) << std::endl;
}
delete[] b;
}
或文本版本:我们通过 new Derived
创建一个基类对象数组。到目前为止一切顺利,这就是大型复杂应用程序所做的(那里的类更加分层,构造函数做得更多),但是全局静态变量(c
)也存在于大型复杂应用程序中,它使用它作为唯一的对象标识符。然后大型复杂的应用程序开始工作,等等。
然后在某个时间点,我们遍历对象数组,做一些工作。迭代看起来与我在这里写的完全相同,在for
循环中找到,具有详尽的指针算法。我在这里的例子只是打印对象id(m
)在大型复杂应用程序中完成更多工作。
但是在大型复杂应用程序中的某个地方发生了一些魔术......在列表的一半之后的某个时间,对象(通过p
指针获得)不再有效,它们的base_id
显示的数据对我来说几乎看起来像指针和其他数据成员的值。但是,如果我检查特定索引处的数组成员,则那里的值是有效的(即:正确增加对象 ID)。
我还检查了以下内容:
- 似乎没有内存损坏问题。瓦尔格林德和克朗的记忆消毒剂没有显示任何可疑的东西。
- 所有对象都已正确创建和销毁,没有内存泄漏。
- 这种疯狂发生的唯一地方是在上面的这个循环中
所以。。。我得出了一个结论:
- 有人在某处修改了一些数组值以实际指向不同类型的对象(所有这些都派生自
Bar
(例如......一些不同名称的基类),因此可能需要在代码中挖掘更多才能找到它。但这是一个巨大的代码库,该数组实际上有数千个引用,在此之前我想问一下:
(问题来了:)
我不知道C++对象可以在运行时更改其大小(如果可能,请与我们分享),因此除了我确定的可能问题之外,社区中的任何人都可能知道为什么指针算术行为如此奇怪?
(是的,该项目将使用标准容器等转换为适当的 C++11 ......但现在我只对这个特定问题感兴趣)
你的问题在这里:
Bar* b = new Foo[200];
b
指向Foo
数组的第一个元素中的Bar
子对象,但不能用作访问数组的一种方式;大小错误。不断将其转换回Foo
指针似乎有效,但容易失败。(面对多重继承,情况会变得更糟,当Bar
子对象甚至与它所属的Foo
对象不在同一地址时。
您发布的代码小心翼翼地始终在执行指针算术之前将b
转换为Foo*
。(即使太小心了:((Foo*)p)->x
可能只是p->x
。但是,如果在任何时候,任何地方,有人忘记这样做(例如尝试b[i]
,或b+i
,或(Foo*)(b+i)
......),这将导致你描述的行为。或者,也许,随着所有这些沮丧的进行,有人将Bar*
降为一个 Baz*
,其中Baz
也继承自Bar
,但大小与Foo
不同。这也将以不稳定的方式覆盖字段。
此外,delete[] b
是未定义的行为。因此,编译器得出的结论并不超出范围,即b
确实指向Bar
实例,并省略或搞砸了Foo*
的强制转换 - 编译器现在做这种事情。
总而言之,Bar* b = new Foo[200];
是行不通的。用
Foo* fp = new Foo[200];
相反。如果出于某种原因,您需要一个 Bar*
,您可以按照它进行操作
Bar* b = fp;
但目前还不清楚为什么需要这个;你可以在需要Bar*
的时候使用fp
。
- 如何创建一个空的全局类并在启动时实例化它
- glfw 在创建之前/创建时实例化窗口位置
- 有没有办法根据命令行参数定义数组大小?运行时与编译时实例化?
- 非类型引用参数可以在运行时修改,这是否意味着模板可以在运行时实例化?
- 根据运行时参数避免模板实例化的代码重复
- 获取派生模板实例化的运行时类型
- 如何根据运行时输入实例化 c++ 模板
- JIT 编译的运行速度能否比编译时模板实例化更快?
- 使用运行时常量实例化的函数模板
- 有没有办法在编译时实例化所有 c++ 模板大小
- 我能否获得一个C++编译器在编译时实例化对象
- C++类实例化是否可以在运行时更改其大小
- 在运行时初始化 boost::accumulator_set
- 在运行时根据不同的类型实例化模板类
- 如何在定义映射/集合时实例化比较函数(函子)
- 运行时初始化 a const
- .NET程序集在运行时初始化
- 位集限制,如何在C++中运行时初始化整数
- 我可以在运行时初始化静态浮点变量吗
- 如何在构造函数接受参数时实例化模板类