C ++中新运算符的返回类型是什么?为什么我们可以将返回值分配给变量(非指针)?

what is the return type of the new operator in c++ & why can we assign the returned value to a variable(non-pointer)?

本文关键字:分配 返回值 变量 指针 我们 运算符 返回类型 为什么 是什么      更新时间:2023-10-16

我知道 c++ 中的 new 运算符总是返回指向所创建对象的指针。

class Node {
    int data;
    Node* next;
    public:
        Node();//default 
        Node(int);//parameter
        Node(int, Node*);//parameter2
        Node(Node*);//copy
        ~Node();
        void setData(int);
        void setNext(Node*);
        int getData();
        Node* getNext();
        void display();
};

这样我们就可以做到:

Node *n = new Node;

但是,我也看到了以下代码:

Node n2 = new Node;

我自己测试了它,我也工作。

这是什么原因呢?

Node(Node*);//copy

这不是复制构造函数,而是从 Node* 到 Node 的隐式转换。

复制构造函数的正确原型是:

Node(Node const&);

对于那些喜欢对编码风格感到愤怒的人来说,这也是一回事:

Node(const Node&);

如果您需要复制构造函数,则很可能还需要赋值运算符:

Node& operator=(Node const&);

如果构造函数接受相同类型的指针,则使该构造函数显式化要安全得多:

explicit Node(Node*);

这会在编译时将赋值从指针捕获到值类型,并可能防止随后的内存泄漏。

Node(Node*);//copy

是一个正确的复制构造函数。普通的复制构造函数通过常量引用来获取参数,即 Node(Node const& src) .

尽管如此,`Node(Node*)仍然存在,当您使用 new Node 中的Node*初始化Node n2时,它是选择的构造函数。这是完美的搭配。

如果您正在使用new和原始指针(即不是std::unique_ptr<Node>(,请注意内存泄漏。当然,您的公共接口不应该暴露Node*

它可能只是因为你创建了一个非常不寻常的复制构造函数:

Node(Node*); //copy

它接受一个指针并从中创建一个对象。因此,您的生产线

Node n2 = new Node;

其实是:

Node n2 = Node( new Node() );

观察返回指针的"新节点",以及对象构造函数Node()。我认为复制构造函数需要隐式,但那是另一种情况。

这是一个不寻常的复制 ctor,因为复制 ctor 通常不带指针,他们这样做正是出于这个原因:它很容易犯愚蠢的错误和拼写错误,比如在类型后忘记*。通常,它们对复制的对象进行常量引用:

Node(Node const&); //copy

这样,这条线

Node n2 = new Node;

不会编译,因为无法将Node*"转换"为Node。另一方面,如果你真的想让那行再次工作,你需要写:

Node n2 = *(new Node);

或类似的东西。尽管如此,这仍然是一个坏主意,因为最近new的 -ed 对象永远不会被删除,并且会永远留在内存中(也就是说,直到程序存在(。

要清理它:

Node* tmp = new Node;
Node n2 = Node(*tmp);
delete tmp;

但无论如何,这都接近于废话,因为您创建一个新的 obejct 只是为了创建一个新对象。这个可怕的事情可能应该改写成简单:

Node n2 = Node();

编辑:这当然与普通相同

Node n2;

除非你的编译器是真正的问题:)

你有一个错误的复制构造函数:

Node(Node*);//copy

这实际上是从Node*Node的隐式转换,绝对不是你想要的。根据定义,复制构造函数应允许在参数中const Node&,例如:

Node( const Node& );

请参阅复制构造函数、赋值运算符和异常安全赋值:

请注意,以下构造函数都不是,尽管事实上 他们可以做与复制构造函数相同的事情,是复制 构造 函数:

MyClass( MyClass* other );
MyClass( const MyClass* other );

这是隐式构造函数转换的情况。

在这种情况下:

Node n = new Node;

将隐式调用Node (Node *);构造函数。

例如,可以在此处找到更完整的解释。

您可以通过使用 them 显式关键字标记此构造函数来防止这种情况发生:

explicit Node (Node *);

现在上面的代码无法编译(g++ 4.7.2(:

错误:请求从"节点*"转换为非标量类型"节点">

你必须显式调用构造函数:

Node n (new Node);

你的代码之所以有效,是因为你的 Node 类定义了一个采用 Node 指针的构造函数。就是这个:

Node(Node*);//copy

顺便说一下,这不是复制构造函数的正确语法,它将是:

Node(const Node& other);

当您使用 Node n2 = new Node;new Node分配给节点对象时,您将 (1( 在堆上分配一个新的 Node 对象,(2( 通过调用采用 Node 指针的构造函数在堆栈上初始化 n2 和 (3( 泄漏堆分配的节点。

动态内存是使用运算符 new 分配的。

Node *n= new Node;

Node 类型的单个元素分配空间,并返回指向该元素的指针

节点

n2=新节点;

由于Node()Node(Node*)功能,在这种情况下工作。

在这种情况下,Node(Node*)正在执行从Node*到节点的隐式转换,因为分配正在执行

新建节点 => 节点(新节点( => 节点(节点*(

通过定义合适的构造函数,可以使用几乎任何东西作为构造函数参数来初始化类型的实例。

此外,由于存在从任何原始指针类型到bool的转换,您还可以执行

bool x = new Node;

并且该语言允许许多其他起初可能看起来令人困惑的内容,例如

Node n = n;

这个想法是,C++程序员更喜欢控制而不是被保姆。因此,在C++中,通常允许未明确禁止的内容,因为在某些情况下,它可能有意义并且有用。其他一些语言基于不同的哲学。


通常(但并非总是(将具有单个参数的构造函数声明为 explicit 是个好主意。那么它就不会被隐式调用。例如,

explicit Node( Node* );

然后声明

Node n = new Node();

将导致编译错误。

但是,显式使用该构造函数,如

Node n( new Node() );

仍将编译。