如果我不明确地初始化类成员,如何初始化?

发布于 2024-09-07 22:22:22 字数 668 浏览 5 评论 0原文

假设我有一个带有私有成员 ptrnamepnamernamecrname 和<代码>年龄。如果我不自己初始化它们会发生什么?这是一个例子:

class Example {
    private:
        int *ptr;
        string name;
        string *pname;
        string &rname;
        const string &crname;
        int age;

    public:
        Example() {}
};

然后我这样做:

int main() {
    Example ex;
}

成员是如何在 ex 中初始化的?指针会发生什么? stringint 使用默认构造函数 string()int() 是否会初始化为 0?参考会员呢?还有 const 引用呢?

我想学习它,这样我就可以编写更好的(无错误)程序。任何反馈都会有所帮助!

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?

I'd like to learn it so I can write better (bug free) programs. Any feedback would help!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

童话 2024-09-14 22:22:22

类中成员的初始化与函数中局部变量的初始化的工作方式相同,而不是显式初始化。

对于对象,将调用它们的默认构造函数。例如,对于 std::string,默认构造函数将其设置为空字符串。如果对象的类没有默认构造函数,那么如果不显式初始化它,将会出现编译错误。

对于原始类型(指针、整数等),它们初始化——它们包含先前发生在该内存位置的任意垃圾。

对于引用(例如std::string&),不初始化它们是非法的,你的编译器会抱怨并拒绝编译这样的代码。引用必须始终被初始化。

因此,在您的具体情况下,如果它们没有显式初始化:

    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

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
熊抱啵儿 2024-09-14 22:22:22

首先,让我解释一下什么是mem-initializer-listmem-initializer-list 是一个以逗号分隔的 mem-initializer 列表,其中每个 mem-initializer 是一个成员名称,后跟(,后跟 表达式列表,后跟 )表达式列表是成员的构造方式。例如,在

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)
    {
    }
};

用户提供的无参数构造函数的 mem-initializer-list 中,为 name(s_str, s_str + 8), rname(name), crname(name),年龄(-4)。这个mem-initializer-list意味着name成员由采用两个输入迭代器的 std::string 构造函数rname 成员通过对 的引用进行初始化namecrname 成员使用对 name 的 const 引用进行初始化,age 成员使用值<代码>-4。

每个构造函数都有自己的mem-initializer-list,并且成员只能按照规定的顺序(基本上是在类中声明成员的顺序)进行初始化。因此,Example的成员只能按照以下顺序初始化:ptr, name, pname, rnamecrnameage

当您不指定成员的mem-initializer时,C++标准表示:

如果实体是类类型的非静态数据成员...,则实体默认初始化 (8.5)。 ...否则,实体未初始化。

在这里,由于 name 是类类型的非静态数据成员,因此如果在 mem-initializer-list中未指定 name 的初始值设定项,则它会默认初始化。 /em>。 Example 的所有其他成员都没有类类型,因此它们未初始化。

当标准说它们未初始化时,这意味着它们可以具有任何值。因此,因为上面的代码没有初始化pname,所以它可以是任何东西。

请注意,您仍然必须遵循其他规则,例如引用必须始终初始化的规则。不初始化引用是一个编译器错误。

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.

浅浅淡淡 2024-09-14 22:22:22

您还可以在声明数据成员时初始化它们:

class another_example{
public:
    another_example();
    ~another_example();
private:
    int m_iInteger=10;
    double m_dDouble=10.765;
};

我几乎专门使用这种形式,尽管我读过一些人认为它是“不好的形式”,也许是因为它最近才被引入 - 我认为是在 C++11 中。对我来说,这更符合逻辑。

新规则的另一个有用的方面是如何初始化本身就是类的数据成员。例如,假设 CDynamicString 是一个封装字符串处理的类。它有一个构造函数,允许您指定其初始值CDynamicString(wchat_t* pstrInitialString)。您很可能将此类用作另一个类中的数据成员 - 例如封装 Windows 注册表值的类,在本例中该类存储邮政地址。要“硬编码”要写入的注册表项名称,请使用大括号:

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;
};

请注意,保存实际邮政地址的第二个字符串类没有初始值设定项,因此将在创建时调用其默认构造函数 - 可能会自动将其设置为空白细绳。

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.

甜味超标? 2024-09-14 22:22:22

如果示例类在堆栈上实例化,则未初始化的标量成员的内容是随机且未定义的。

对于全局实例,未初始化的标量成员将被清零。

对于本身就是类实例的成员,将调用它们的默认构造函数,因此您的字符串对象将被初始化。

  • int *ptr; //未初始化的指针(如果全局则归零)
  • string name; //调用构造函数,用空字符串初始化
  • string *pname; //未初始化的指针(如果全局则归零)
  • string &rname; //如果未能初始化此
  • const string &crname; //编译错误如果未能初始化此
  • int Age; //标量值,未初始化且随机(如果全局则归零)

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 string
  • string *pname; //uninitialized pointer (or zeroed if global)
  • string &rname; //compilation error if you fail to initialize this
  • const string &crname; //compilation error if you fail to initialize this
  • int age; //scalar value, uninitialized and random (or zeroed if global)
败给现实 2024-09-14 22:22:22

这取决于类的构造方式

回答这个问题需要理解 C++ 语言标准中的一个巨大的 switch case 语句,而对于普通人来说很难有直觉。

举一个简单的例子来说明事情有多困难:

main.cpp

#include <cassert>

int main() {
    struct C { int i; };

    // This syntax is called "default initialization"
    C a;
    // i undefined

    // This syntax is called "value initialization"
    C b{};
    assert(b.i == 0);
}

在默认初始化中,您将从以下位置开始: https://en.cppreference.com/w/cpp/language/default_initialization 我们转到“默认初始化的效果是”部分并开始 case 语句:

  • “if T is a 非POD”:否(POD的定义本身就是一个巨大的开关语句)
  • “如果 T 是数组类型”:否
  • “否则,什么也不做”:因此它留下了一个未定义的值

然后,如果有人决定值初始化,我们会转到 https://en.cppreference.com/w/cpp/language/value_initialization “值初始化的效果是”并启动case 语句:

  • “如果 T 是没有默认构造函数或具有用户提供或删除的默认构造函数的类类型”:情况并非如此。您现在将花 20 分钟谷歌搜索这些术语:
    • 我们有一个隐式定义的默认构造函数(特别是因为没有定义其他构造函数)
    • 它不是用户提供的(隐式定义的)
    • 未删除(=删除
  • “如果 T 是具有既不是用户提供也不是删除的默认构造函数的类类型”:是

这就是为什么我强烈建议您永远不要依赖“隐式”零初始化。除非有强有力的性能原因,否则请显式初始化所有内容,无论是在构造函数上(如果您定义了),还是使用聚合初始化。否则,你会给未来的开发人员带来非常非常大的风险。

It depends on how the class is constructed

Answering this question comes understanding a huge switch case statement in the C++ language standard, and one which is hard for mere mortals to get intuition about.

As a simple example of how difficult thing are:

main.cpp

#include <cassert>

int main() {
    struct C { int i; };

    // This syntax is called "default initialization"
    C a;
    // i undefined

    // This syntax is called "value initialization"
    C b{};
    assert(b.i == 0);
}

In default initialization you would start from: https://en.cppreference.com/w/cpp/language/default_initialization we go to the part "The effects of default initialization are" and start the case statement:

  • "if T is a non-POD": no (the definition of POD is in itself a huge switch statement)
  • "if T is an array type": no
  • "otherwise, nothing is done": therefore it is left with an undefined value

Then, if someone decides to value initialize we go to https://en.cppreference.com/w/cpp/language/value_initialization "The effects of value initialization are" and start the case statement:

  • "if T is a class type with no default constructor or with a user-provided or deleted default constructor": not the case. You will now spend 20 minutes Googling those terms:
    • we have an implicitly defined default constructor (in particular because no other constructor was defined)
    • it is not user-provided (implicitly defined)
    • it is not deleted (= delete)
  • "if T is a class type with a default constructor that is neither user-provided nor deleted": yes

This is why I strongly recommend that you just never rely on "implicit" zero initialization. Unless there are strong performance reasons, explicitly initialize everything, either on the constructor if you defined one, or using aggregate initialization. Otherwise you make things very very risky for future developers.

舞袖。长 2024-09-14 22:22:22

未初始化的非静态成员将包含随机数据。实际上,它们只会具有分配给它们的内存位置的值。

当然,对于对象参数(如string),对象的构造函数可以进行默认初始化。

在你的例子中:

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

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
作妖 2024-09-14 22:22:22

具有构造函数的成员将调用其默认构造函数进行初始化。

您不能依赖其他类型的内容。

Members with a constructor will have their default constructor called for initialisation.

You cannot depend on the contents of the other types.

小猫一只 2024-09-14 22:22:22

如果它在堆栈上,则没有自己的构造函数的未初始化成员的内容将是随机且未定义的。即使它是全球性的,依赖它们被归零也是一个坏主意。无论它是否在堆栈上,如果一个成员有自己的构造函数,就会调用它来初始化它。

因此,如果您有 string* pname,则指针将包含随机垃圾。但对于字符串名称,将调用字符串的默认构造函数,为您提供一个空字符串。对于您的引用类型变量,我不确定,但它可能是对某些随机内存块的引用。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文