为什么调用重载的构造函数会导致调用默认构造函数?

时间:2017-11-25 01:45:38

标签: c++

我的C ++程序总是调用比我想要的更多的构造函数。它应该创建一个边,然后通过重载边构造函数的字符串输入自动创建两个节点。

但它首先调用节点对象的默认构造函数,然后调用重载的自定义构造函数。析构函数不会立即删除默认节点。这导致我的程序出错。

问题是程序应该计算对象的数量并分配与对象数量相关的适当ID。

编译器输出

Default-Constructor
Default-Constructor
Overload-Constructor
ID: 1 numInstances: 2
Destructor
Overload-Constructor
ID: 2 numInstances: 1
Destructor
Edge:  -> Node_0002

Destructor
Destructor
Program ended with exit code: 0

以下是一个片段,供您查看最有可能导致错误的代码:

的main.cpp

int main() {
    Edge e1 = Edge("1", "2");
    std::cout << "Edge: " << e1.toString() << endl;
    return 0;
}

node.hpp

double Node::numInstances = 0;
Node::Node()
{
    numInstances++;
    cout << "Default-Constructor" << endl;
    double idNumber = numInstances;
    m_id = setUpIdString(idNumber);
}

Node::Node(string id)
{
    double idNumber = getNumberFromString(id);
    cout << "Overload-Constructor" << endl;
    cout << "ID: " << idNumber << " numInstances: " << numInstances << endl;
    if (idNumber > numInstances) {
        numInstances++;
        m_id = setUpIdString(idNumber);
    }
}

Node::~Node()
{
    numInstances--;
    cout << "Destructor" << endl;
}

edge.cpp

Edge::Edge(string src, string dst)
{
    m_srcNode = Node(src);
    m_dstNode = Node(dst);
}

编辑:

node.hpp

class Node
{
public:
    Node();
    Node(string id);
    ~Node();
    string getId();

private:
    string m_id;
    static double numInstances;
};

edge.hpp

class Edge
{
public:
    Edge(Node& rSrc, Node& rDst);
    Edge(string src, string dst);
    string toString();
    Node& getSrcNode();
    Node& getDstNode();

private:
    Node m_srcNode;
    Node m_dstNode;
};

2 个答案:

答案 0 :(得分:2)

构造函数与C ++中的其他函数不同。构造函数的工作是初始化一个对象。为了确保以合理的方式初始化对象,需要初始化所有成员对象(和基础对象)。 *这在构造函数体的左大括号之前发生,这样当你在构造函数体中时,一切都处于合理的状态。

为了确保以您想要的方式发生这种情况,您可以使用initializer list(请注意,在初始化各种容器时,此术语也指其他内容;如果您之前听过与之相关的术语,这不是我在这里讨论的内容)。在函数签名之后,您放置一个冒号,然后是您的类的每个成员(按照它们在类中声明的顺序)以及您希望如何初始化它。例如,如果您有以下struct

struct A {
    int i;
    char c;
    std::string s;
    A();
};

您可以声明将构造函数定义为

A::A() : i{17}, c{'q'}, s{"Hello, mrb! Welcome to Stack Overflow"}
{
    // Nothing to do in the body of the constructor
}

这样,A对象的成员在函数启动之前初始化为17'q'和问候语,并且未初始化任何其他方式。

由于您不这样做,编译器会使用节点的默认构造函数。然后,在构造函数体内创建其他节点,并将它们分配给类中的节点。

答案 1 :(得分:0)

m_srcNode = Node(src);
m_dstNode = Node(dst);

这里,首先使用默认构造函数构造两个对象,然后使用Node(src)Node(dst)调用重载的构造。之后,调用隐式定义的复制分配器,将临时对象分配给m_srcNodem_dstNode。最后,临时对象被破坏。

如果您想避免额外的构造函数调用,可以编写member initializer list

Edge::Edge(string src, string dst) : m_srcNode(src), m_dstNode(dst) {}