简体   繁体   中英

How to correctly use smart pointers inside of a class

I am creating a binary tree in C++, and I have some problems when using smart pointers inside of the node class. When using normal pointers, everything works fine, but with smart pointers it is just not working. I think that the problem is in this line in the insert method:

    '''
    binaryNode* node = this; // This is working
    std::shared_ptr<binaryNode> node {this}; // This throws "double free or corruption" error
    std::shared_ptr<binaryNode> node = shared_from_this (); // This throws "bad weak ptr error", I am correctly inheriting from  enable_shared_from_this
    '''

How can I replicate binaryNode* node = this; with smart pointers? I even tried to use public std::enable_shared_from_this<binaryNode> usuccessfully. Thanks for your help!

Edit: I will try to explain myself a little bit better. This is the insert() function of a binary search tree, that looks like this (this is the.cpp file):

'''
#include "binarynode.h"

binaryNode::binaryNode(int value){
    this->value = value;
    this->right = nullptr;
    this->left = nullptr;
}

void binaryNode::insert(int value){

binaryNode* node = this;
while(true){
    if(value > node->value){
        if(node->right != nullptr){
            node = node->right;
        }else{
            node->right = new binaryNode(value);
            break;
        }
    }else if(value < node->value){
        if(node->left != nullptr){
            node = node->left;
        }else{
            node->left = new binaryNode(value);
            break;
        }
    }else{
        return;
     }
  }

How can I replicate this using smart pointers?

Edit 2: This is my.h file:

'''
#ifndef BINARYNODE_H
#define BINARYNODE_H

class binaryNode
{
public:
    int value;
    binaryNode(int value);
    binaryNode* right;
    binaryNode* left;
    void insert(int value);
};

#endif // BINARYNODE_H

And this is the main file:

#include <iostream>
#include "binarynode.h"

using namespace std;

void printTree(binaryNode* node){
    if(node == nullptr) return;
    cout << node->value << endl;
    printTree(node->left);
    printTree(node->right);
}

int main(){
    binaryNode* bn = new binaryNode(9);
    bn->insert(4);
    bn->insert(20);
    bn->insert(1);
    bn->insert(6);
    bn->insert(15);
    bn->insert(170);
    printTree(bn);
    return 0;
}
  1. You cannot directly convert the same raw pointer to a shared pointer more than once, because then you will have several owners that know nothing about each other, each one thinking it has full control over the object. This is why std::shared_ptr<binaryNode> node {this} gives you a double-delete.
  2. You also cannot use shared_from_this unless there is at least one shared pointer already pointing to your object. This is why std::shared_ptr<binaryNode> node = shared_from_this () doesn't work.

If you want shared pointers, make them all shared. For example:

 // binaryNode* bn = new binaryNode(9); <-- nope!
 auto bn = std::make_shared<binaryNode>(9);

 // binaryNode* node = this; <-- nope!
 std::shared_ptr<binaryNode> node = shared_from_this();

I do not recommend using shared pointers here though. Unique pointers are more appropriate.

You do not need to use shared_ptr<>.

Actually smart pointer are here to 'solve' ownership on object, thus when an object has a single owner, unique_ptr<> should be used, and when ownership is shared, shared_ptr are used. In you situation, the ownership is clear, each node owns its left and right members, thus unique_ptr can be used.

For the tree traversal problem, don't mess with smart pointers as you are not requesting any ownership, but just looking at values, thus raw pointers are ok.

So you may end up with something like this:

#include <memory>
#include <iostream>

struct binaryNode {
    binaryNode(int value) : value(value) {}
    void insert(int value);

    int value = 0;
    std::unique_ptr<binaryNode> right;
    std::unique_ptr<binaryNode> left;
};

void binaryNode::insert(int value){

    binaryNode* node = this;
    while(true){
        if(value > node->value){
            if(node->right != nullptr){
                node = node->right.get();
            }else{
                node->right = std::make_unique<binaryNode>(value);
                break;
            }
        }else if(value < node->value){
            if(node->left != nullptr){
                node = node->left.get();
            }else{
                node->left = std::make_unique<binaryNode>(value);
                break;
            }
        }else{
            return;
        }
    }
}

void printTree(const binaryNode &node){
    std::cout << node.value << std::endl;
    if (node.left)
        printTree(*node.left);
    if (node.right)
        printTree(*node.right);
}

int main(){
    auto bn = std::make_unique<binaryNode>(9);
    bn->insert(4);
    bn->insert(20);
    bn->insert(1);
    bn->insert(6);
    bn->insert(15);
    bn->insert(170);
    printTree(*bn);
    return 0;
}

You may notice that the print does not need to take a pointer, it can work on reference.

C++ vector can be used to support recursive data structure. It's much simpler that use smart ptr. Basicly In your Node store vector children as a member.

#include <vector>

using std::vector;


struct Node {
    Node() = default;
    Node(const Node &) = delete;
    Node(Node &&) = default;

    vector<Node> children;
};

int main()
{
    Node root;
    root.children.push_back(Node());
    root.children.push_back(Node());
    root.children[0].children.push_back(Node());
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM