C++:When creating a new objects inside a function and returning it as result, must I use the new operator to create the object?

Question 1

Node* func() { Node n; Node* ptr=&n; return n;}

Your code creates a local Node instance (on the stack), then returns its address. When the function returns, the Node instance, being a local variable, is destroyed. The address the function returned now points to some memory with undefined contents, and any attempts at dereferencing this pointer will lead to undefined behavior.

In order to create a node, you actually need to call a Node constructor. How you want to return the result is relevant to how you call the constructor.

  • You can either return a pointer as you were trying to do, in which case you need to use the new operator:

      Node* func() { 
        Node* n = new Node(10); 
        return n;
      }
    

    However, when you do this, you give func callers the responsibility to destroy the object in question. Since new and delete are symmetrical operations, it is considered better form to put them in symmetrical places in your code, e.g. like this:

      void cnuf(Node* p) { 
        delete p; 
      }
    

    A better alternative altogether may be to use std::shared_ptr which gives you reference counting, like this:

      std::shared_ptr<Node> func() {
        return std::make_shared<Node>(10);
      }
    

    Using this approach, the callers do not need to manually manage each node's lifecycle. Another alternative is using std::unique_ptr instead, which only allows single object ownership.

  • Or you can return the node by value, in which case you create it locally, and then let the function return mechanisms make a copy when you return it:

      Node func() { 
        Node n(10); 
        return n;
      }
    

Question 2

You can declare a destructor like this in your Node class declaration:

class Node {
  ...
  ~Node();
}

Then, you can define it like this:

Node::~Node() {
  ...
}

However, it is probably better to actually let the list managed the connection between its Node instances (next field), and only let the Node class manage the lifecycle of its member data (data field)


You can return pointer to local object, but it will be pointed to stack memory, so results may be suprising. Look the following code:

#include <iostream>

using namespace std;

class Node { public: int n; };

Node* create(int n) {
    Node node = Node();
    node.n = n;
    cout << "Created " << node.n << endl;
    return &node;
}

int main() {
   Node* n1 = create(10);
   Node* n2 = create(20);
   cout << "Reading " << n1->n << endl;
   cout << "Reading " << n2->n << endl;
   return 0;
}

You won't get "10" "20" output. Instead

Created 10
Created 20
Reading 20
Reading 1891166112

First object was destructed (when first create function call ended). Second object was created on top of destructed n1, so n1 address was equal to n2 address.

Compiler will warn you when you return stack addresses:

main.cpp: In function Node* create(int):
main.cpp:8:10: warning: address of local variable node returned [-Wreturn-local-addr]
     Node node = Node();