问题
Suppose I have a class with private memebers ptr
, name
, pname
, rname
, crname
and age
. What happens if I don\'t initialize them myself? Here is an example:
class Example {
private:
int *ptr;
string name;
string *pname;
string &rname;
const string &crname;
int age;
public:
Example() {}
};
And then I do:
int main() {
Example ex;
}
How are the members initialized in ex? What happens with pointers? Do string
and int
get 0-intialized with default constructors string()
and int()
? What about the reference member? Also what about const references?
What else should I know about?
Does anyone know a tutorial that covers these cases? Maybe in some books? I have access in university\'s library to a lot of C++ books.
I\'d like to learn it so I can write better (bug free) programs. Any feedback would help!
回答1:
In lieu of explicit initialization, initialization of members in classes works identically to initialization of local variables in functions.
For objects, their default constructor is called. For example, for std::string
, the default constructor sets it to an empty string. If the object's class does not have a default constructor, it will be a compile error if you do not explicitly initialize it.
For primitive types (pointers, ints, etc), they are not initialized -- they contain whatever arbitrary junk happened to be at that memory location previously.
For references (e.g. std::string&
), it is illegal not to initialize them, and your compiler will complain and refuse to compile such code. References must always be initialized.
So, in your specific case, if they are not explicitly initialized:
int *ptr; // Contains junk
string name; // Empty string
string *pname; // Contains junk
string &rname; // Compile error
const string &crname; // Compile error
int age; // Contains junk
回答2:
First, let me explain what a mem-initializer-list is. A mem-initializer-list is a comma-separated list of mem-initializers, where each mem-initializer is a member name followed by (
, followed by an expression-list, followed by a )
. The expression-list is how the member is constructed. For example, in
static const char s_str[] = "bodacydo";
class Example
{
private:
int *ptr;
string name;
string *pname;
string &rname;
const string &crname;
int age;
public:
Example()
: name(s_str, s_str + 8), rname(name), crname(name), age(-4)
{
}
};
the mem-initializer-list of the user-supplied, no-arguments constructor is name(s_str, s_str + 8), rname(name), crname(name), age(-4)
. This mem-initializer-list means that the name
member is initialized by the std::string constructor that takes two input iterators, the rname
member is initialized with a reference to name
, the crname
member is initialized with a const-reference to name
, and the age
member is initialized with the value -4
.
Each constructor has its own mem-initializer-list, and members can only be initialized in a prescribed order (basically the order in which the members are declared in the class). Thus, the members of Example
can only be initialized in the order: ptr
, name
, pname
, rname
, crname
, and age
.
When you do not specify a mem-initializer of a member, the C++ standard says:
If the entity is a nonstatic data member ... of class type ..., the entity is default-initialized (8.5). ... Otherwise, the entity is not initialized.
Here, because name
is a nonstatic data member of class type, it is default-initialized if no initializer for name
was specified in the mem-initializer-list. All other members of Example
do not have class type, so they are not initialized.
When the standard says that they are not initialized, this means that they can have any value. Thus, because the above code did not initialize pname
, it could be anything.
Note that you still have to follow other rules, such as the rule that references must always be initialized. It is a compiler error to not initialize references.
回答3:
You can also initialize data members at the point you declare them:
class another_example{
public:
another_example();
~another_example();
private:
int m_iInteger=10;
double m_dDouble=10.765;
};
I use this form pretty much exclusively, although I have read some people consider it 'bad form', perhaps because it was only recently introduced - I think in C++11. To me it is more logical.
Another useful facet to the new rules is how to initialize data-members that are themselves classes. For instance suppose that CDynamicString
is a class that encapsulates string handling. It has a constructor that allows you specify its initial value CDynamicString(wchat_t* pstrInitialString)
. You might very well use this class as a data member inside another class - say a class that encapsulates a windows registry value which in this case stores a postal address. To 'hard code' the registry key name to which this writes you use braces:
class Registry_Entry{
public:
Registry_Entry();
~Registry_Entry();
Commit();//Writes data to registry.
Retrieve();//Reads data from registry;
private:
CDynamicString m_cKeyName{L"Postal Address"};
CDynamicString m_cAddress;
};
Note the second string class which holds the actual postal address does not have an initializer so its default constructor will be called on creation - perhaps automatically setting it to a blank string.
回答4:
If you example class is instantiated on the stack, the contents of uninitialized scalar members is random and undefined.
For a global instance, uninitialized scalar members will be zeroed.
For members which are themselves instances of classes, their default constructors will be called, so your string object will get initialized.
int *ptr;
//uninitialized pointer (or zeroed if global)string name;
//constructor called, initialized with empty stringstring *pname;
//uninitialized pointer (or zeroed if global)string &rname;
//compilation error if you fail to initialize thisconst string &crname;
//compilation error if you fail to initialize thisint age;
//scalar value, uninitialized and random (or zeroed if global)
回答5:
Uninitialized non-static members will contain random data. Actually, they will just have the value of the memory location they are assigned to.
Of course for object parameters (like string
) the object's constructor could do a default initialization.
In your example:
int *ptr; // will point to a random memory location
string name; // empty string (due to string's default costructor)
string *pname; // will point to a random memory location
string &rname; // it would't compile
const string &crname; // it would't compile
int age; // random value
回答6:
Members with a constructor will have their default constructor called for initialisation.
You cannot depend on the contents of the other types.
回答7:
If it is on the stack, the contents of uninitialized members that don't have their own constructor will be random and undefined. Even if it is global, it would be a bad idea to rely on them being zeroed out. Whether it is on the stack or not, if a member has its own constructor, that will get called to initialize it.
So, if you have string* pname, the pointer will contain random junk. but for string name, the default constructor for string will be called, giving you an empty string. For your reference type variables, I'm not sure, but it'll probably be a reference to some random chunk of memory.
来源:https://stackoverflow.com/questions/3127454/how-do-c-class-members-get-initialized-if-i-dont-do-it-explicitly