两种类型的构造函数之间有什么区别?第一个使用“:”,第二个使用大括号定义,例如函数

时间:2019-04-10 17:00:53

标签: c++

我在为类定义构造函数时遇到问题,而且我以前定义它们的方式也无法正常工作。

这是针对edx c ++中级课程的。当我尝试定义一个继承自其他类的类时,使用构造函数引用我的基类无法正常工作,因此无法正确定义我的新类。第一个代码中第一次使用构造函数(使用可以正常工作的“:”表示法)和第二个代码中第二个构造函数(定义类似函数,我之前已经正确使用并且在这里不起作用)之间有什么区别?

我有一个名为Person的基类和一个从基类继承的Student类。当我尝试初始化一个调用Person类构造函数之一的Student对象时,它给出错误的答案。我认为这是因为我定义构造函数的方式。我将它们设为函数,并在花括号中初始化变量。我之前使用过这种方法,并且可以正常工作,但是在这里不起作用。但是在花括号前使用“:”的标准方法在这里可以正常工作。我想知道这两者之间有什么区别?

Person.h:

#pragma once
#include <string>

class Person
{
private:
    std::string name;
protected:
    int age;
public:
    Person();
    Person(const std::string & name, int age);
    void displayNameAge() const;
};

Person.cpp:

#include "pch.h"
#include "Person.h"
#include <iostream>

//Person::Person()
//  : name("[unknown name]"), age(0)
//{
//  std::cout << "Hello from Person::Person()" << std::endl;
//}
Person::Person()
{
    name = "[unknown name]";
    age = 0;
    std::cout << "Hello from Person::Person()" << std::endl;
}

Person::Person(const std::string & name, int age)
{
    this->name = name;
    this->age = age;
    std::cout << "Hello from Person::Person(string, int)" << std::endl;
}

//Person::Person(const std::string & name, int age)
//  : name(name), age(age)
//{
//  std::cout << "Hello from Person::Person(string, int)" << std::endl;
//}


void Person::displayNameAge() const
{
    std::cout << name << ", " << age << std::endl;
}

Student.h:

#pragma once
#include "Person.h" 

class Student : public Person
{
private:
    std::string course;
public:
    Student();
    Student(const std::string & name, int age, const std::string & course);
void displayCourse() const;
};

Student.cpp:

#include "pch.h"
#include "Student.h"
#include <iostream>

Student::Student()
{
    course = "[unassigned course]";
    std::cout << "Hello from Student::Student()" << std::endl;
}

// first method: the right one
//Student::Student(const std::string & name, int age, const std::string & course)
//  : Person(name, age), course(course)
//{
//  std::cout << "Hello from Student::Student(string, int, string)" << std::endl;
//}
// second method: the wrong one
Student::Student(const std::string & name, int age, const std::string & course)
{
    Person(name, age);
    this->course = course;
    std::cout << "Hello from Student::Student(string, int, string)" << std::endl;
}

void Student::displayCourse() const
{
    std::cout << course << std::endl;
}

Main.cpp:

#include "pch.h"
#include "Student.h"
 int main()
 {
     // Create a Student object using the no-argument constructor.
     Student Student1;
     Student1.displayNameAge();
     Student1.displayCourse();

     // Create a Student object using the parameterized constructor.
     Student Student2("Jane Smith", 25, "Physics");
     Student2.displayNameAge();
     Student2.displayCourse();

     return 0;
 }

预期结果:

Hello from Person::Person()
Hello from Student::Student()
[unknown name], 0
[unassigned course]
Hello from Person::Person(string, int)
Hello from Student::Student(string, int, string)
Jane Smith, 25
Physics

实际结果:

Hello from Person::Person()
Hello from Student::Student()
[unknown name], 0
[unassigned course]
Hello from Person::Person()
Hello from Person::Person(string, int)
Hello from Student::Student(string, int, string)
[unknown name], 0
Physics

1 个答案:

答案 0 :(得分:1)

初始化列表

您缺少的是初始化列表。

Type::Type(Parameters)
    : member1(init)       // This is the initializer list
    , member2(init)
{
    Your code
}

如果您未明确提供一个,则编译器将使用父类的默认构造函数为您完成此工作,然后为每个成员调用默认构造函数。

所以让我们看看你的课程。

Student::Student(const std::string & name, int age, const std::string & course)
{
     // Code
}

那是你写的。但这就是编译器实现的。

Student::Student(const std::string & name, int age, const std::string & course)
    : Person()
    , course()
{
     // Code
}

因此,由于您没有执行任何操作,因此编译器将其调用添加到Person默认构造函数和course (std::string)默认构造函数中。

现在,如果您的基类没有默认构造函数,就会出现问题。然后,编译器无法添加适当的调用,并且会生成编译器错误。

但是还有一个问题,就是您编写此方法的效率很低,因为您基本上将所有成员初始化两次。调用默认构造函数,然后在 Code 部分中,使用另一个值重新初始化成员。

Student::Student(const std::string & name, int age, const std::string & course)
    : Person(name, age)
    , course()                       // Initialize it to empty here.
{
    course = "[unassigned course]";  // Re-Initialize it with info.
}

您只需执行一次:

Student::Student()
    : Person()                        // Note person must have a default constructor for this to work.
    , course("[unassigned course]")
{}

临时对象

Student::Student(const std::string & name, int age, const std::string & course)
{
    Person(name, age);
    // CODE.
}

这没有按照您的想法做。
让我们添加初始化列表。

Student::Student(const std::string & name, int age, const std::string & course)
    : Person()
    , course()
{
    Person(name, age);   // This is creating a person object localy.
                         // the person object has no name so is a
                         // temporary variable and goes out of scope
                         // at the ';'. So it is created and destroyed
                         // in place before other code is executed.
                         //
                         // This does not help initialize the class.
                         // It is creating a completely different
                         // and independent object.
    // CODE.
}

您可以在此处查看执行情况:

Hello from Person::Person()                       // Init parent
Hello from Person::Person(string, int)            // Init the temp object.
                                                  // If you put a print in the destructor
                                                  // You will also see that executed
                                                  // Before the Student
                                                  // This is also why the object has "unknown name" and 0 age. 

Hello from Student::Student(string, int, string)  // Now we init the student
[unknown name], 0
Physics

建议

是否存在需要初始化对象版本的有效方案?我个人认为不会(如果有的话就忽略它),所以要摆脱PersonStudent的默认构造函数。这样就无法创建未初始化的Students或`People。