C++ 2011: Uniform Initialization

In C++, initializing objects is not done in the same manner in all contexts. The rules for initialization and constructors are not really simple and developers sometimes have to write lot of explicit code for initializing objects. Fortunately the new C++ standard comes to the rescue, offering a uniform initialization syntax and semantics.

The way it was before C++ 2011

Let's take the following structure as a start point.

struct Color
{
    unsigned char A;
    unsigned char R;
    unsigned char G;
    unsigned char B;
};

If you want to initialize an object of this type, then you can write:

Color c;
c.A = 0xff;
c.R = 0xff;
c.G = 0x00;
c.B = 0x00; 


But there is a simpler way to write that:

Color c = {0xff, 0xff, 0x00, 0x00};

If you want to zero the entire structure, then you can write:

Color c = {0};

However, you cannot use this initialization type to initialize all the members with 0xff, for instance. If you write

Color c = {0xff};

then only A will be 0xff, the other fields would be initialized with a default value, which is 0.

Since this is a POD (Plain Old Data) type (which is a scalar or a POD class - a class with no user defined constructors or destructor, no user defined copy assignment operator, no base classes or virtual functions, no private or protected non-static data and no static data that is non-POD types) you could also write:

memset(&c, 0, sizeof(c));

Mind you, initializing with 0 with memset works for any POD type, however, initializing with another value, say 1, will not have the expected result; if A, R, G and B would have been ints for instance, because memset puts the specified value in every byte starting at the specified address for the specified count, you'd get ints that are 0x01010101 instead of simply 1.

When it comes to array you can initialize all the elements of the array with 0 as in:

Color ca[2] = {0};

which is identical to

Color ca[2] = {{0}, {0}};

However, if you want to initialize with something other than 0, then you can write:

Color ca[2] = {{0}, {0xff, 0xff, 0x00, 0x00}};

Now, another way to initialize Color would be to define a constructor (which makes the type non-POD, so no more memset). The default value of the constructor parameters allow you to declare arrays of this object. Otherwise, since there is no default constructor, it wouldn't be possible to declare an array of Color.

struct Color
{
    unsigned char A;
    unsigned char R;
    unsigned char G;
    unsigned char B;
 
    Color(
       unsigned char a = 0, 
       unsigned char r = 0,
       unsigned char g = 0,
       unsigned char b = 0): A(a), R(r), G(g), B(b)
    {}
};

In this case you can say:

Color c(0xff, 0xff, 0x00, 0x00);

However, since Color is no longer an aggregate (because it has a user defined constructor) this initialization is no longer supported; the following issues errors:

Color c = {0xff, 0xff, 0x00, 0x00};

Color ca[2] = {{0}, {0xff, 0xff, 0x00, 0x00}};

If instead of using an array, we'd want to use a vector of Color, then initializing it would be only possible like this:

vector<Color> cv;
cv.push_back(Color(0));
cv.push_back(Color(0xff, 0xff, 0x00, 0x00));

Or

vector<Color> ca(2);
ca[1] = Color(0xff, 0xff, 0x00, 0x00);

Or

ca[1].A = 0xff;
ca[1].R = 0xff;
ca[1].G = 0x00;
ca[1].B = 0x00;

Or if you'd have an array of Color, then you can say:

Color ca[2] = {Color(0), Color(0xff, 0xff, 0x00, 0x00)};
vector<Color> cv(ca, ca+2);

Enter C++ 2011

The new standard introduces a new way of initializing objects, in a more uniform manner. Even though Color is a non-POD type you can write:

Color c {0xff, 0xff, 0x00, 0x00};

Color ca[2] {{0}, {0xff, 0xff, 0x00, 0x00}};

vector<Color> ca {{0}, {0xff, 0xff, 0x00, 0x00}};

Or (these two being identical)

Color c = {0xff, 0xff, 0x00, 0x00};

Color ca[2] = {{0}, {0xff, 0xff, 0x00, 0x00}};

vector<Color> ca = {{0}, {0xff, 0xff, 0x00, 0x00}};

Or with a map:

map<string, Color> colors = {
  {"red",     {0xff, 0xff, 0x00, 0x00}},
  {"green",   {0xff, 0x00, 0xff, 0x00}},
  {"blue",    {0xff, 0x00, 0x00, 0xff}},
};

It is important to note that the initialization with the parenthesis, as shown in the previous paragraph as the only way to initialize non-POD types before C++ 2011, is still available.

Also note that in the following code sample, the first vector will have three elements (initialized with the default value), while the second will have a single element with value 3.

vector vec1(3);
vector vec2{3};

If the array or the vector were members of a class, the following initialization could be done:

class foo
{
   int data[3];
public:
   foo(): data{1,2,3} {}
};

class bar
{
   vector data;
public:
   bar(): data{1,2,3} {}
};

What if the array would be dynamically allocated on the heap? Then, you can use the same initialization syntax with {}:

int* data = new int[3] {1,2,3};

Or, in case it is part of a class

class foobar
{
   int* data;
public:
   foobar(): data(new int[3]{1,2,3}){}
};

It is also possible to use the curly braces to initialize an object in a return statement:

Color make_color(unsigned char r, unsigned char g, unsigned char b)
{
   return {0xff, r, g, b};
}

Or for a function call:

void use_color(Color c)
{
}

use_color({0xff, 0xff, 0x00, 0x00});

Conclusions

The new C++ standard brings a new, uniform initialization syntax and semantics, helping developers to write simpler and uniform code. Using the curly braces {} constructs the same value in all contexts where it is legal. Unfortunately, not all compilers support this new feature at the time of writing this article, but they will pretty soon.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值