该篇文章是用于梳理在学习聚合类的相关文章过程中总结归纳的相关知识点。用于后续深入理解和使用聚合类进行准备。
首先看一下C++标准中的定义:
An aggregate is an array or a class (clause 9) with no user-declared constructors (12.1),
no private or protected non-static data members (clause 11),
no base classes (clause 10), and no virtual functions (10.3).
由以上定义看出满足以下条件的类才可以称之为聚合类
下面我们举一个简单的例子进行说明:
我们可以提供一个花括号扩起来的成员初始值列表,并用它初始化聚合类的数据成员,初始值的顺序必须与声明的顺序一致,初始值列表的元素个数少于类的成员数量,则靠后的成员被值初始化。
struct Person
{
std::string name;
int height;
};
int main()
{
Person person = {
"xiaohong",10};
std::cout << person.name<<" "<<person.height << std::endl;
system("pause");
return 0;
}
以下我们在例举一些典型的非聚合类和聚合类:
// 非聚合类
class NotAggregate1
{
virtual void f() {
} //定义了虚函数
};
class NotAggregate2
{
int x; //定义了非静态的私有属性
};
class NotAggregate3
{
public:
NotAggregate3(int) {
} 自定义了构造函数
};
// 聚合类
class Aggregate1
{
public:
NotAggregate1 member1; //公共成员
Aggregate1& operator=(Aggregate1 const & rhs) {
/* */} //重载赋值运算符
private:
void f() {
} // 私有函数
};
聚合类的的主要特性是可以使用**{}**符号像数组一样进行初始化。
先从数据的初始化进行说明,数组初始化的形式如下:
Type array_name[n] = {a1, a2, …, am};
if (m == n)
array_name的第i个元素由ai进行初始化
else if (m < n)
前m个元素由1~m个a进行初始化,m-n个元素由'value-initialized'实现初始化
else if (m > n)
编译器报错
else (类似数组定义形式: int a[] = {
1, 2, 3};)
数组的长度m = = n
下面解释一下什么是值初始化:
当一个标量类型(bool、int、char、double、pointers 等)的对象被值初始化时,这意味着它被初始化为 0 表示该类型(false 表示 bool,0.0 表示 double 等)。当具有用户声明的默认构造函数的类类型对象进行值初始化时,调用其默认构造函数。这个定义不精确,有点不正确,但它应该给你基本的概念。不能对引用进行值初始化。 例如,如果类没有合适的默认构造函数,则非聚合类的值初始化可能会失败。
下面例举一些典型例子,用于说明数组初始化:
class A
{
public:
A(int) {
} //无默认构造函数
};
class B
{
public:
B() {
} //默认构造函数
};
int main()
{
A a1[3] = {
A(2), A(1), A(14)}; //正常 n == m
A a2[3] = {
A(2)}; //错误 类A没有默认构造函数. 无法对a2[1]和a2[2]进行值初始化
B b1[3] = {
B()}; //正常 b1[1]和b1[2]可以通过默认构造函数进行值初始化
int Array1[1000] = {
0}; //所有元素初始化为0;
int Array2[1000] = {
1}; //第一个元素初始化为1,其它全部为0;
bool Array3[1000] = {
}; //所有的值被初始化为false
int Array4[1000]; //没有初始化,数组元素都是不确定的值;
int array[2] = {
1, 2, 3, 4}; //错误, 初始化值个数大于数组长度
}
现在让我们看看如何用大括号初始化聚合类。几乎和数组的值初始化相同的方式。我们将按照在类定义中出现的顺序初始化非静态数据成员(根据定义,它们都是公共的)。如果初始化器比成员少,则其余的都是值初始化的。如果初始值设定项少于成员数,则其余的都是值初始化的。如果无法对未显式初始化的成员之一进行值初始化(如数据类型不匹配),则会出现编译时错误。如果初始值设定项过多,我们也会收到编译时错误。
如下面代码中所示:
struct X
{
int i1;
int i2;
};
struct Y
{
char c;
X x;
int i[2];
float f;
protected:
static double d;
private:
void g(){
}
};
Y y = {
'a', {
10, 20}, {
20, 30}};
在上面的例子中y.c用’a’初始化,y.x.i1用10,y.x.i2用20,y.i[0]用20,y.i[1]用30和y.f是值初始化的,即用0.0初始化。 受保护的静态成员 d 根本没有初始化,因为它是静态的。
聚合联合的不同之处在于,可以只用大括号初始化它们的第一个成员。如果在 C++ 方面足够先进,甚至可以考虑使用联合(它们的使用可能非常危险,必须仔细考虑。
现在我们知道了聚合的特别之处,让我们尝试了解对类的限制; 也就是说,他们为什么在那里。 我们应该明白,带大括号的成员初始化意味着该类只不过是其成员的总和。如果存在用户定义的构造函数,则意味着用户需要做一些额外的工作来初始化成员,因此大括号初始化将是不正确的。如果存在虚函数,则意味着该类的对象(在大多数实现中)具有指向该类的所谓 vtable 的指针,该指针是在构造函数中设置的,因此大括号初始化是不够的。