在自己的构造中使用对象有什么问题吗?

use*_*715 13 c++ c++11

我正在存储需要对对象进行操作的操作,但我不想使用继承.所以我使用的技术是拥有一个非成员函数,它接受一个指向对象的指针,然后将它存储在对象中,如下所示:

struct command
{
    command()
    {
    }

    command(const std::function<void()>& action)
        : action(action)
    {
    }

    int n;
    std::function<void()> action;
};

void test_action(command* this_ptr)
{
    this_ptr->n = 5;
}


int main()
{
    command com(std::bind(test_action, &com));
    com.action();
    std::cout << com.n;
}
Run Code Online (Sandbox Code Playgroud)

我的问题是安全command com(std::bind(test_action, &com));吗?还是未定义的行为?

Mar*_* A. 7

首先:什么是对象?

[intro.object]\1

[...]一个物体是一个存储区域[...]

在对象的生命周期开始之前分配存储:

[basic.life]

在对象的生命周期开始之前但在对象将占用的存储被分配之后[...],可以使用任何指向对象所在或将被定位的存储位置的指针,但仅限于有限的方式.对于正在建造或销毁的物体,见12.7 [构造和破坏].否则,这样的指针指的是已分配的存储(3.7.4.2),并且使用指针就像指针的类型为void*一样,是明确定义的.

因此指针指的是分配的空间,使用它没有任何害处.您只需要一个堆栈地址,任何编译器都应该能够正确识别它.在此特定实例中,不需要对象本身的初始化操作.

这是有道理的,因为在经典的AST时尚编译器中,如果你看一下声明符的标准层次结构,就像一个简单的玩具代码一样

class command {
public:
  command(int) {
  }
};

int funct(command*) {
    return 2;
}

int main() {
    command com(funct(&com));
}
Run Code Online (Sandbox Code Playgroud)

这条线

command com(funct(&com));
Run Code Online (Sandbox Code Playgroud)

解释如下:

[dcl.decl]

simple-declaration:
    attribute-specifier-seqopt decl-specifier-seqopt init-declarator-listopt;
       ...
         initializer:
            brace-or-equal-initializer
                ( expression-list ) // The declaration statement is already specified
Run Code Online (Sandbox Code Playgroud)

最后,对于你的代码,这是gcc如何编译这一行(-O0)

command com(std::bind(test_action, &com));

->

movq    %rax, -104(%rbp)
leaq    -104(%rbp), %rdx
leaq    -96(%rbp), %rcx
movl    test_action(command*), %esi
movq    %rcx, %rdi
movq    %rax, -136(%rbp)        # 8-byte Spill
movq    %rcx, -144(%rbp)        # 8-byte Spill
callq   _ZSt4bindIRFvP7commandEJS1_EENSt12_Bind_helperIT_JDpT0_EE4typeEOS5_DpOS6_
leaq    -80(%rbp), %rax
movq    %rax, %rdi
movq    -144(%rbp), %rsi        # 8-byte Reload
movq    %rax, -152(%rbp)        # 8-byte Spill
callq   _ZNSt8functionIFvvEEC1ISt5_BindIFPFvP7commandES5_EEEET_NSt9enable_ifIXntsr11is_integralISA_EE5valueENS1_8_UselessEE4typeE
movq    -136(%rbp), %rdi        # 8-byte Reload
movq    -152(%rbp), %rsi        # 8-byte Reload
callq   command::command(std::function<void ()> const&)
Run Code Online (Sandbox Code Playgroud)

它是:只是来自基指针的一堆堆栈地址,它们在调用构造函数之前传递给绑定函数.

如果您在构造之前尝试使用该对象,那么事情会有所不同(虚函数表可能会变得棘手).

旁注:如果您正在复制或通过值传递对象并超出范围(并且仍然将地址保留到堆栈位置),则不保证这是安全的.另外:如果编译器决定将其存储(对于任何架构/原因)作为基础框架的偏移量,您可能会关闭到未定义的行为区域.

  • 我的意思是,所有汇编程序证明这个特定的编译器在这个特定的实例中生成了"安全"的代码.对于这是否是UB这个问题完全没有帮助.所需要的是来自标准(或等效)的引用,该引用解释了`&com`在这种情况下是一个有效的指针. (6认同)