8

我有一个包含许多函数指针的类,我希望在构造对象时将它们全部初始化为 NULL。为此,我计划在从第一个指针到最后一个指针的内存位置上使用 memset,但是,我不确定这是否能 100% 工作。

是否保证如果这些函数指针在类中连续声明,它们的内存位置也将是连续的。我假设填充不会影响我正在尝试做的事情,因为任何填充字节也只会设置为 NULL 。

示例类实现

class C
{
private:
    void (*func1)();
    void (*func2)();
    void (*func3)();
    void (*func4)();
};
4

4 回答 4

10

保证它们按照声明的顺序以递增的地址出现。对于没有干预访问说明符的数据成员而言,这通常是正确的,因此如果类中有其他数据成员,那么它们可以干预的唯一方法就是那里有访问说明符。

我认为修改填充字节不能保证安全。我认为不能保证实现不会在数据成员之间放置“重要的东西”,尽管我不能立即想到实现想要放在那里的任何东西。为设计奇特的精确标记 GC 输入信息?用于测试缓冲区溢出的可识别值?

不能保证所有位为零表示空函数指针。

您可以使用以下方法处理全位零表示的问题:

std::fill(&func1, &func4 + 1, (void(*)(void))0);

但这仍然会留下填充问题。保证数组中没有填充,但(按照标准)在类中没有。您的实现使用的 ABI 可能会在必要的程度上指定结构布局,以确保上述类的布局与 4 个函数指针的数组相同。

另一种方法是执行以下操作:

struct function_pointers {
    void (*func1)();
    void (*func2)();
    void (*func3)();
    void (*func4)();
};

class C : private function_pointers
{
public:
    C() : function_pointers() {}
};

初始化程序function_pointers()规定(因为它没有用户声明的构造函数)的成员function_pointers是零初始化的,即使C它本身的实例只是默认初始化的。function_pointers可以是数据成员而不是基类,如果您更喜欢输入更多内容来访问func1等。

请注意,C现在在 C++03 中是非 POD。在 C++11C中,在此更改之后仍然是标准布局,但如果在 中定义了任何数据成员,则不会是标准布局C,并且它不是一个平凡的类。因此,如果您依赖 POD/标准/琐碎性,请不要这样做。而是保留C原样的定义并使用聚合初始化 ( C c = {0};) 将 C 的实例初始化为零。

于 2013-03-15T11:13:46.877 回答
3

先做这个。

class C
{
public:
    C() : func1(nullptr), func2(nullptr), func3(nullptr), func4(nullptr)
    { };
private:
    void (*func1)();
    void (*func2)();
    void (*func3)();
    void (*func4)();
};
于 2013-03-15T11:13:04.103 回答
1

除非你真的知道你在做什么,否则永远不要在 C++ 中这样做。这很容易出错,并且您的代码的未来维护者(甚至可能是您!)可能不会认识到所有存在的缺陷,并且事情可能会出现可怕的错误。实现此目的的 C++ 方法是编写一个构造函数,该构造函数正确初始化函数指针nullptr(如果您使用 C++11)或0.

于 2013-03-15T11:12:28.950 回答
1

永远不要在非平凡的对象上使用 memset,尤其是那些多态的对象(具有虚函数或从具有虚方法的基类派生等)。如果你这样做,你会炸毁指向vtable的vptr!灾难!

vptr & vtable用于实现多态行为,因此尊重那些隐藏的类成员。

当我们谈论位和字节时应该使用 memset,而不是谈论对象时。

尊重非平凡的对象,对 memset 说不 :)

于 2013-03-15T11:32:26.717 回答