【问题标题】:Different ways of initializing an object in c++在 C++ 中初始化对象的不同方法
【发布时间】:2018-09-22 22:17:49
【问题描述】:

想象一下这个类:

class Entity {
public:
    int x, y;
    
    Entity() : x(0), y(0) { }
    Entity(int x, int y) : x(x), y(y) { }
}

这里有多种用我认为我知道的初始化类的方法:

Entity ent1;                //Uses the default constructor, so x=0 and y=0
Entity ent2();              //Uses the default constructor, so x=0 and y=0 (Not sure)
Entity ent3(1, 2);          //Made constructor, so x=1 and y=2
Entity ent4 = Entity();     //Default constructor, so x=0 and y=0
Entity ent5 = Entity(2, 3); //Made constructor, so x=2 and y=3

我知道可以在堆内存上创建一个对象,但这不是我目前正在寻找的。​​p>

我的问题是,这些初始化对象的方式有什么区别?

我不确定何时应该使用哪一个。

【问题讨论】:

  • Entity ent2(); 是一个函数声明。
  • 不,这意味着根本不存在Entityent2是一个函数。
  • 不,这意味着您正在声明一个名为 ent2 的函数,它返回一个 Entity
  • 您可以在 C++11 及更高版本中使用 {}。查找统一初始化。您提供的示例是 C++87(并且 MVP 可能允许变量 ent2,编译器不一致)
  • @JesperJuhl 这不是我的“个人”昵称。它基于 Stroustroup 的书籍第一期。本质上是他在 1986-1987 年首次描述的语言 IS C++87 。因为没有其他说明

标签: c++ class c++11 constructor initialization


【解决方案1】:
Entity ent1;

上面的语句使用Entity类的默认构造函数。


Entity ent2();

如果可能的话,上面的声明将被编译器视为函数原型。它被称为 most vexing parse (MVP) 的案例,它的存在导致出现误导性的“聪明的愚蠢规则”:“永远不要使用括号”。


在这样的语句中,为ent3 调用用户定义的构造函数:

Entity ent3(1, 2);

MVP 可以罢工的另一种情况是这样的:

Entity ent3_1(int(a), int(b));  // It's not what it looks like.

ent3_1 以上不是变量。该语句声明了一个带有两个 int 参数的函数。 int(a)int a 相同是 C 语言和声明语法的遗留问题。


Entity ent4 = Entity();

ent4ent2正确 版本,直到 C++11。默认构造函数作为值初始化的一部分被调用。它的形式允许避免使ent2ent3_1 不正确的歧义求解原则。这里的等号不是赋值,因为这里不会发生operator= 调用。它是声明语法的一部分,用于标记初始化表达式。


Entity ent5 = Entity(2, 3);

ent5 是 ent3 case 的一个版本。作为值初始化的一部分调用的用户定义的构造函数。


你的问题被标记为C++11,C++11允许统一初始化语法:

Entity ent12{};     // This is a legal alternative of ent2 case
Entity ent13{1, 2}; // A call to constructor or member initialization
Entity ent13{ int(a), int(b) }; // Not a function anymore
Entity ent14 = {};              // Not an assignment
Entity ent15 = Entity{2, 3};    // Not an assignment either!

请注意,统一初始化语法有一个警告。例如。这一行

std::vector<int> v(10); 

声明一个包含 10 个元素的向量。但是这个

std::vector<int> v{10};

声明一个使用 int 类型的单个元素初始化的向量,其值为 10。这是因为 std::vector 有一个定义了以下签名的构造函数:

vector( std::initializer_list<T> init, const Allocator& alloc = Allocator() );

如果您不能在不触发 MVP 的情况下使用 () 或在不调用不需要的构造函数的情况下使用 {},则值初始化赋值语法可以解决此问题。

附录:必看CppCon 2018: Nicolai Josuttis “The Nightmare of Initialization in C++”

【讨论】:

  • 您能否详细解释一下(){} 之间的区别,我应该如何让程序以不同的方式解释它们?我自己的类是否有可能有一个构造函数,它在使用{} 调用时执行某些操作,而在使用() 调用时执行其他操作?
  • @CătălinaSîrbu () 或 {} 之间的区别在于提供参数与统一初始化。上面实际上给出了这可能导致不同行为的示例,但这是完全独立的问题。您不能使用任意形式的大括号内容来做到这一点。定义采用std::initializer_list&lt;T&gt; 的构造函数是上述技巧的关键,因为初始化列表的 T 类型元素将默认绑定到该代理类。滥用它可能会导致混乱。
  • 不错的答案,但我错过了关于 = ... 表单需要复制构造函数的存在和可访问性的观点,即使省略了复制构造函数调用。
  • @cmaster-reinstatemonica 真的吗? wandbox.org/permlink/CNYt9MBNllYPP1aY
  • 关键是,至少在旧标准中,副本是否可以省略并不重要,复制构造函数必须存在且可访问,因为副本是语义。 IE。编译器检查复制构造函数是否存在且可访问,并且只有当该测试为肯定时,它才会继续忽略对复制构造函数的调用。我认为他们已经改变/计划在最新标准中改变这一点,但是直到 C++11,我已经验证它仍然是这种情况。
猜你喜欢
  • 1970-01-01
  • 2011-02-26
  • 1970-01-01
  • 2020-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-21
  • 1970-01-01
相关资源
最近更新 更多