读书频道 > 网站 > 网页设计 > 深入应用C++11:代码优化与工程级应用
1.3.2 列表初始化的使用细节
15-07-07    下载编辑
收藏    我要投稿   
在StackOverflow的最近一次世界性调查中,C++11在所有的编程语言中排名第二, C++11受到程序员的追捧是毫不意外的,因为它就像C++之父Bjarne Stroustrup说的:它看起来就像一门新的语言。C++11新增加了相当多的立即去当当网订购

在C++11中,初始化列表的使用范围被大大增强了。一些模糊的概念也随之而来。

上一节,读者已经看到了初始化列表可以被用于一个自定义类型的初始化。但是对于一个自定义类型,初始化列表现在可能有两种执行结果:

struct A
{
   int x;
   int y;
} a = { 123, 321 };   // a.x = 123, a.y = 321

struct B
{
   int x;
   int y;
   B(int, int) : x(0), y(0) {}
} b = { 123, 321 };   // b.x = 0, b.y = 0

其实,上述变量a的初始化过程是C++98/03中就有的聚合类型(Aggregates)的初始化。它将以拷贝的形式,用初始化列表中的值来初始化struct A中的成员。

struct B由于定义了一个自定义的构造函数,因此,实际上初始化是以构造函数进行的。

看到这里,读者可能会希望能够有一个确定的判断方法,能够清晰地知道初始化列表的赋值方式。

具体来说,在使用初始化列表时,对于什么样的类型C++会认为它是一个聚合体?

下面来看看聚合类型的定义:

(1)类型是一个普通数组(如int[10]、char[]、long[2][3])。

(2)类型是一个类(class、struct、union),且

无用户自定义的构造函数。

无私有(Private)或保护(Protected)的非静态数据成员。

无基类。

无虚函数。

不能有{ }和=直接初始化(brace-or-equal-initializer)的非静态数据成员。

对于数组而言,情况是很清晰的。只要该类型是一个普通数组,哪怕数组的元素并非一个聚合类型,这个数组本身也是一个聚合类型:

int x[] = { 1, 3, 5 };
float y[4][3] =
{
   { 1, 3, 5 },
   { 2, 4, 6 },
   { 3, 5, 7 },
};
char cv[4] = { 'a', 's', 'd', 'f' };

std::string sa[3] = { "123", "321", "312" };

下面重点介绍当类型是一个类时的情况。首先是存在用户自定义构造函数时的例子,代码如下:

struct Foo
{
   int x;
   double y;
   int z;
   Foo(int, int) {}
};
Foo foo { 1, 2.5, 1 }; // error

这时无法将Foo看做一个聚合类型,因此,必须以自定义的构造函数来构造对象。

私有(Private)或保护(Protected)的非静态数据成员的情况如下:

struct ST
{
   int x;
   double y;
protected:
   int z;
};
ST s { 1, 2.5, 1 }; // error

struct Foo
{
   int x;
   double y;
protected:
   static int z;
};
Foo foo { 1, 2.5 }; // ok

在上面的示例中,ST的初始化是非法的。因为ST的成员z是一个受保护的非静态数据成员。

而Foo的初始化则是成功的,因为它的受保护成员是一个静态数据成员。

这里需要注意,Foo中的静态成员是不能通过实例foo的初始化列表进行初始化的,它的初始化遵循静态成员的初始化方式。

对于有基类和虚函数的情况:

struct ST
{
   int x;
   double y;
   virtual void F(){}
};
ST s { 1, 2.5 };    // error

struct Base {};
struct Foo : public Base
{
   int x;
   double y;
};
Foo foo { 1, 2.5 }; // error

ST和Foo的初始化都会编译失败。因为ST中存在一个虚函数F,而Foo则有一个基类Base。

最后,介绍“不能有{ }和=直接初始化(brace-or-equal-initializer)的非静态数据成员”这条规则,代码如下:

struct ST
{
   int x;
   double y = 0.0;
};
ST s { 1, 2.5 };   // error

在ST中,y在声明时即被=直接初始化为0.0,因此,ST并不是一个聚合类型,不能直接使用初始化列表。

在C++98/03中,对于y这种非静态数据成员,本身就不能在声明时进行这种初始化工作。但是在C++11中放宽了这方面的限制。可以看到,在C++11中,非静态数据成员也可以在声明的同时进行初始化工作(即使用{ }或=进行初始化)。

对于一个类来说,如果它的非静态数据成员在声明的同时进行了初始化,那么它就不再是一个聚合类型,因此,也不能直接使用初始化列表。

对于上述非聚合类型的情形,想要使用初始化列表的方法就是自定义一个构造函数,比如:

struct ST
{
   int x;
   double y;
   virtual void F(){}
private:
   int z;
public:
   ST(int i, double j, int k) : x(i), y(j), z(k) {}
};
ST s { 1, 2.5, 2 };

需要注意的是,聚合类型的定义并非递归的。简单来说,当一个类的非静态成员是非聚合类型时,这个类也有可能是聚合类型。比如下面这个例子:

struct ST
{
   int x;
   double y;
private:
   int z;
};
ST s { 1, 2.5, 1 }; // error

struct Foo
{
   ST st;
   int x;
   double y;
};
Foo foo { {}, 1, 2.5 }; // OK

可以看到,ST并非一个聚合类型,因为它有一个Private的非静态成员。

但是尽管Foo含有这个非聚合类型的非静态成员st,它仍然是一个聚合类型,可以直接使用初始化列表。

注意到foo的初始化过程,对非聚合类型成员st做初始化的时候,可以直接写一对空的大括号“{ }”,相当于调用ST的无参构造函数。

现在,对于使用初始化列表时的一些细节有了更深刻的了解。对于一个聚合类型,使用初始化列表相当于对其中的每个元素分别赋值;而对于非集合类型,则需要先自定义一个合适的构造函数,此时使用初始化列表将调用它对应的构造函数。

点击复制链接 与好友分享!回本站首页
分享到: 更多
您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:1.3 功能
下一篇:1.5 小结
相关文章
图文推荐
JavaScript网页动画设
1.9 响应式
1.8 登陆页式
1.7 主题式
排行
热门
文章
下载
读书

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训
版权所有: 红黑联盟--致力于做最好的IT技术学习网站