简体   繁体   中英

C++ Primitive Type Initialization v.s. Object Initialization

I'm curious of how C++ primitive built in types, int for instance, are initialized in comparison to objects of a class.

After doing research I learned that C++ is a statically typed language meaning type checking is done at compile-time not run-time. This means, that primitive types are not objects of a class and a types cannot change.

So what differs in the following object initializations:

class Foo
{
public:
    int num;
    Foo(int n) : num(n) {}
};

Foo bar(5);
Foo bar2{ 5 };
Foo bar3 = 5;

To the primitive initializations:

int num(5);
int num2{ 5 };
int num3 = 5;

I know instances of Foo call the constructor to initialize but what does the primitive call or do to initialize if it is not an object?

Furthermore what else fundamentally distinguishes the types Foo & int, other than Foo being "a user defined blue print to create instances" and int being "a non object built in type".

Class initialization

For a class with user-defined constructors like Foo , the only difference between the initializations is that the last (called copy-initialization ) is equivalent to

Foo bar3 = Foo(5);

That is, the initializer expression is converted to the class type ( via a constructor) and then bar3 is initialized from that . (Until C++17 this notionally involves a copy or move, but compilers routinely avoid that overhead.)

Note that there is one more syntax:

Foo bar4={5};

This is known as copy-list-initialization , but the only difference from the version without the = is that explicit constructors are disallowed.

Structural initialization

The lack of significant differences between these forms fails to motivate having them and is therefore confusing. The distinction comes with containers , broadly construed so as to include aggregates (simple C-like struct s). Those support {} -initialization with a different meaning, which is to construct an object containing some data, rather than computed from it. So

std::vector<double> a(10,1),b{10,1};

defines a to have 10 values (which are each 1) and b to have the two values 10 and 1.

It has been suggested that use of braces to initialize should be restricted to this meaning to avoid confusion.

Primitive initialization

Finally, we have your int examples. It should be clear now that all the initializations are interchangeable (except that list-initialization prohibits narrowing conversions), since there is no distinction between computing a primitive value and filling it. Nor is there any possibility of observing the initialization of (or assignment to) a variable of primitive type: it simply comes to have the value supplied as an atomic action of the abstract machine. (Compilers know this and elide as many such variables as they can for efficiency.)

Objects

Even an int is an object (in the C++ sense of that word, not the Java or Python sense), and when you have a Foo you have two objects: the Foo itself and the int num it contains (which is created and destroyed along with it). So your initializations of Foo objects are also two initializations each: the outer one consisting of the inner one (which is always performed by the num(n) member-initializer ).

It is the existence of this extra object that distinguishes Foo from int , without having to define a class operationally in terms of a memory layout: the operations allowed on a Foo are in general different because it is distinct from any object(s) it might contain.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM