C++初始化列表构造函数VS普通构造函数

Posted 烧饼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++初始化列表构造函数VS普通构造函数相关的知识,希望对你有一定的参考价值。

普通构造函数VS初始化列表构造函数

初始化列表构造函数最优先匹配问题

对于一个类而言,只要其中包含有初始化列表的构造函数,编译器在编译使用语法的构造时会最倾向于调用初始化列表构造函数,哪怕做类型转换也在所不惜,哪怕有类型最佳匹配的普通构造函数或移动构造函数也会被劫持

class Widget 
public:
	Widget(int i, bool b);
	Widget(int i, double d);
	Widget(std::initializer_list<long double> il);
	operator float() const; 
;

Widget w1(10, true); 	// 使⽤小括号初始化
						//调⽤第⼀个构造函数
Widget w210, true; 	// 使⽤花括号初始化
						// 调⽤第三个构造函数
						// (10 和 true 转化为long double)
Widget w3(10, 5.0);		// 使⽤小括号初始化
						// 调⽤第二个构造函数
Widget w410, 5.0;		// 使⽤花括号初始化
						// 调⽤第三个构造函数
						// (10 和 5.0 转化为long double)
Widget w5(w4); 			// 使⽤小括号,调⽤拷⻉构造函数
Widget w6w4; 			// 使⽤花括号,调⽤std::initializer_list构造函数
Widget w7(std::move(w4)); // 使⽤小括号,调⽤移动构造函数
Widget w8std::move(w4); // 使⽤花括号,调⽤std::initializer_list构造函数

编译器这种热衷于把括号初始化与初始化列表构造函数匹配的行为,会导致一些莫名其妙的错误

class Widget 
public:
	Widget(int i, bool b);
	Widget(int i, double d);
	Widget(std::initializer_list<bool> il); // element type is now bool
	… // no implicit conversion funcs
;
Widget w10, 5.0; //错误!要求变窄转换,int(10)double(5.0)无法转换为bool类型

只有在没有办法把中实参的类型转化为初始化列表时,编译器才会回到正常的函数决议流程中

⽐如我们在构造函数中⽤std::initializer_list<std::string>代替std::initializer_list<bool> ,这时⾮std::initializer_list构造函数将再次成为函数决议的候选者,因为没有办法把int和bool转换为std::string:

class Widget 
public:
Widget(int i, bool b);
Widget(int i, double d);
Widget(std::initializer_list<std::string> il);
…
;
Widget w1(10, true); // 使⽤小括号初始化,调⽤第⼀个构造函数
Widget w210, true; // 使⽤花括号初始化,调⽤第⼀个构造函数
Widget w3(10, 5.0); // 使⽤小括号初始化,调⽤第⼆个构造函数
Widget w410, 5.0; // 使⽤花括号初始化,调⽤第⼆个构造函数

空初始化列表会发生什么

假如内是空的,类中既有默认构造函数,也有初始化列表构造函数,此时会被视为没有实参,而不是一个空的初始化列表,因此会调用默认构造函数。如果就是想调用初始化列表构造函数,这应该使用的方式

class Widget 
public:
Widget();
Widget(std::initializer_list<int> il);
...
;
Widget w1; // 调⽤默认构造函数
Widget w2; // 同上
Widget w3(); // 最令⼈头疼的解析!声明⼀个函数
Widget w4(); // 调⽤std::initializer_list
Widget w5; // 同上

初始化列表带来的vector的坑

std::vector<int> v1(10, 20); 	//使⽤⾮std::initializer_list
								//构造函数创建⼀个包含10个元素的std::vector
								//所有的元素的值都是20
std::vector<int> v210, 20; 	//使⽤std::initializer_list
								//构造函数创建包含两个元素的std::vector
								//元素的值为10和20

初始化列表构造函数问题带来的两点启示

  • 作为类库作者,如果在构造函数中重载了一个或多个初始化列表构造函数,要考虑用户使用初始化的情况,最好避免类似std::vector中的情况。应该尽可能做到用户无论用小括号还是花括号进行初始化都不会产生区别。一定要慎重考虑新出现的初始化列表构造函数对其他构造函数的影响!!!
  • 作为类库使用者,必须认真的考虑小括号和花括号之间选择创建对象的方式,最好选择其中一个从一而终

构造函数中的 C++ 初始化列表

【中文标题】构造函数中的 C++ 初始化列表【英文标题】:C++ initialization list in constructor 【发布时间】:2014-10-11 02:23:11 【问题描述】:

我正在尝试使用另一个名为“List”的类的构造函数中的初始化列表来初始化名为“Winery”的类的实例。问题是,当我将 Winery 构造函数交给一个要复制的酒厂时,它无法复制信息。

这是 Winery 类的头文件:

class Winery

public:

    Winery(const char * const name, const char * const location, const int acres, const int rating);
    virtual ~Winery(void);

    const char * const getName() const  return name; 
    const char * const getLocation() const  return location; 
    const int getAcres() const  return acres; 
    const int getRating() const  return rating; 

private:
    char    *name;
    char    *location;
    int     acres;
    int     rating;
;

这是我的 List 类的头文件的相关部分:

struct Node
    
        Node(const Winery& winery);     
        Winery item;                                            
        Node *nextByName;               
        Node *nextByRating;             
    ;

这是我的 List 类中的构造函数:

List::Node::Node(const Winery& winery) :
item(winery.getName(), winery.getLocation(), winery.getAcres(), winery.getRating()),
nextByName(nullptr),
nextByRating(nullptr)


在我看来,我似乎正在做我需要做的一切。我传递给构造函数的酒厂数据成员是私有的,所以我试图通过获取信息的函数来获取它们。他们的顺序和一切都是正确的。初始化它们后指针工作得很好,但信息不存在,所以我真的不知道该怎么做。如果您想知道,这是一个分配,我们必须使用初始化列表(我已经尝试过没有它们,但它也不起作用,所以我真的不知道该怎么做)。我将不胜感激任何帮助!谢谢!

编辑:这是我的酒厂构造函数:

Winery::Winery(const char * const name, const char * const location, const int acres, const int rating) :
acres(acres),
rating(rating)

    char *newName = new char[sizeof(name) + 1];
    char *newLocation = new char[sizeof(location) + 1];

【问题讨论】:

您必须定义“无法复制信息”的含义,因为这可能意味着任何事情。析构函数是什么样的?我的猜测是析构函数正在删除 newNamenewLocation 字符串。您是否有理由不使用 const char* 而不是 std::string 我的意思是当构造函数遍历完初始化列表后,我在内存中查看itemnextByNamenextByRatingnextByNamenextByRating 已初始化为 NULL,但 item 的所有四个部分仍然具有 Visual Studio 默认内存值(0xcdcdcdcd、0xfeeffeef 等)以及我使用 @987654336 的原因@ 是因为我们被禁止使用std::string 【参考方案1】:

从外观上看,这几行:

char *newName = new char[sizeof(name) + 1];
char *newLocation = new char[sizeof(location) + 1];

基本上什么都不做,因为locationname 字符串没有分配甚至写入,这可能是问题的根源。但是,您的 acresrating 应该已正确构造。

这是我创建的工作版本(ideone here -> http://ideone.com/v98zpq)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

class Winery

public:
    Winery(const char * const name, const char * const location, const int acres, const int rating) :
        name(strdup(name)),
        location(strdup(location)),
        acres(acres),
        rating(rating)
    
    

    virtual ~Winery(void)
    
        free(name);
        free(location);
    

    const char * const getName() const  return name; 
    const char * const getLocation() const  return location; 
    const int getAcres() const  return acres; 
    const int getRating() const  return rating; 

private:
    char    *name;
    char    *location;
    int     acres;
    int     rating;
;

struct Node

    Node(const Winery& winery);
    Winery item;
;

Node::Node(const Winery& winery) :
    item(winery.getName(), winery.getLocation(), winery.getAcres(), winery.getRating())



int main()

    Winery winery("Mission Hill Winery", "Kelowna, BC, Canada", 646, 4);

    Node node(winery);

    printf("%s\n", node.item.getName());
    printf("%s\n", node.item.getLocation());
    printf("%i\n", node.item.getAcres());
    printf("%i\n", node.item.getRating());

输出:

Mission Hill Winery
Kelowna, BC, Canada
646
4

【讨论】:

以上是关于C++初始化列表构造函数VS普通构造函数的主要内容,如果未能解决你的问题,请参考以下文章

c++ 初始化列表VS构造函数内赋值初始化成员变量

c++ 初始化列表VS构造函数内赋值初始化成员变量

C++ 构造函数初始化列表

C++中,继承时,创建子类对象,能否在子类构造函数初始化列表里调用基类构造函数?

构造函数中的 C++ 初始化列表

为啥 C++ 列表初始化也会考虑常规构造函数?