Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/132.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在对象自身的构造中使用对象有什么问题吗?_C++_C++11 - Fatal编程技术网

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

C++ 在对象自身的构造中使用对象有什么问题吗?,c++,c++11,C++,C++11,我正在存储一个需要对对象进行操作的操作,但我不想使用继承。因此,我使用的技术是让一个非成员函数接受指向对象的指针,然后将其存储在对象中,如下所示: struct command { command() { } command(const std::function<void()>& action) : action(action) { } int n; std::function<voi

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

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;
}
struct命令
{
命令()
{
}
命令(const std::function&action)
:行动
{
}
int n;
功能动作;
};
无效测试动作(命令*此ptr)
{
该ptr->n=5;
}
int main()
{
命令com(std::bind(test_action,&com));
com.action();

std::cout首先:什么是对象

[介绍对象]\1

[…]对象是一个 储存区域[……]

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

[基本生活]

在对象的生存期开始之前但在存储之后 对象将占用的对象已分配[…]任何 指对象将位于或曾经位于的存储位置 可以使用,但只能以有限的方式使用。对于正在建造或破坏的对象,请参见12.7[建造和破坏]。否则, 此类指针指的是已分配的存储(3.7.4.2),并使用指针,就像指针为void*类型一样, 定义明确

因此,指针指的是分配的空间,使用它没有坏处。您只需要请求堆栈地址,任何编译器都应该能够正确地计算出来。在这个特定实例中,不需要对象本身的初始化操作

这是有意义的,因为在一个经典的AST编译器中,如果您想查看声明器的标准层次结构,可以使用一个简单的玩具代码,如

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

int funct(command*) {
    return 2;
}

int main() {
    command com(funct(&com));
}
线路

command com(funct(&com));
其解释如下:

[dcl.decl]

最后,对于您的代码,这是gcc编译这一行(-O0)的方式

命令com(std::bind(test_action,&com));
->
movq%rax,-104(%rbp)
leaq-104(%rbp),%rdx
leaq-96(%rbp),%rcx
movl测试动作(命令*),%esi
movq%rcx,%rdi
movq%rax,-136(%rbp)#8字节溢出
movq%rcx,-144(%rbp)#8字节溢出
调用ZSt4bindIRFvP7commandEJS1\u-EENSt12\u-Bind\u-helperIT\u-JDpT0\u-EE4typeEOS5\u-DpOS6_
leaq-80(%rbp),%rax
movq%rax,%rdi
movq-144(%rbp),%rsi#8字节重新加载
movq%rax,-152(%rbp)#8字节溢出
调用函数IfVveeC1 ist5\u BindIfPfVp7命令5\u Eeet\u NST9启用\u IfxNTSR11是完整的\u Ee5值1\u 8\u可使用请参见4类型
movq-136(%rbp),%rdi#8字节重新加载
movq-152(%rbp),%rsi#8字节重新加载
callq command::command(std::function const&)
也就是说:在调用构造函数之前,只需要从基指针传递到绑定函数的一组堆栈地址

如果您在构建对象之前实际尝试使用该对象,情况会有所不同(使用虚拟函数表可能会变得棘手)


旁注:如果复制对象或按值传递对象并超出范围(并且仍然保留堆栈位置的地址),则不能保证安全。另外:如果编译器决定存储它(无论出于何种架构/原因)作为与基本帧的偏移量,您可能会偏离到未定义的behaviorland。

是的,一旦对象被声明(以及分配的存储空间)但尚未初始化,您就可以使用指向该对象的指针。访问对象本身[以非常受限的方式除外]提供未定义的行为;但您不能这样做。C++11 3.8/5对此进行了描述:

在对象的生存期开始之前,但在对象将占用的存储被分配之后[…]可以使用指向对象将要或曾经所在的存储位置的任何指针,但只能以有限的方式使用。[…]使用指针就像指针是
void*
类型一样,是定义良好的


您只需将其传递给
bind
,它将指针值复制到绑定函数包装器中,该包装器将其视为使用它,就好像它是
void*

一样,只要您在构建对象之前不实际处理它,您就是黄金。正如@MarcoA所评论的,您只使用了在此之前的地址。这是正确的如果你想经常这样做的话,保护复制和移动是个好主意。+1,尽管与偶然事件争论是完全错误的好方法。我同意,而且我通常不会从“它编译”或“它工作”中得出任何观察结果.未定义的行为是一个棘手的问题。无论如何,我认为在这种情况下没有任何伤害。如果我遗漏了什么,请告诉我,我会编辑post@OliverCharlesworth只是添加了一些细节,我的意思是,汇编程序所证明的是,这个特定的编译器生成了“安全的”此特定实例中的代码。这对是否为UB的问题毫无帮助。需要的是引用标准(或等效标准),说明
&com
在此场景中是有效指针。无耻插头:
simple-declaration:
    attribute-specifier-seqopt decl-specifier-seqopt init-declarator-listopt;
       ...
         initializer:
            brace-or-equal-initializer
                ( expression-list ) // The declaration statement is already specified
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&)