简体   繁体   English

如何避免在 C++ 中使用 new 运算符?

[英]How to avoid using new operator in C++?

I have a C++ program that creates Huffman codes for all characters in file.我有一个 C++ 程序,它为文件中的所有字符创建霍夫曼代码。 It works good, but I want to create nodes without using new operator because I know that you shouldn't use it.它运行良好,但我想在不使用 new 运算符的情况下创建节点,因为我知道你不应该使用它。 I tried using a vector global variable for saving nodes but that doesn't work.我尝试使用矢量全局变量来保存节点,但这不起作用。

std::vector<Node> nodes;

Node* create_node(unsigned char value, unsigned long long counter, Node* left, Node* right) {

    Node temp;
    temp.m_value = value;
    temp.m_counter = counter;
    temp.m_left = left;
    temp.m_right = right;

    nodes.push_back(temp);
    return &nodes[nodes.size() - 1];
}

Edit: I added more code, I did't really explained what doesn't work.编辑:我添加了更多代码,我没有真正解释什么不起作用。 Problem is in generate_code() , it never reaches nullptr.问题出在generate_code()中,它永远不会到达 nullptr。 I also tried using Node and not Node* but the same thing happened.我也尝试使用 Node 而不是 Node* 但同样的事情发生了。

void generate_code(Node* current, std::string code, std::map<unsigned char, std::string>& char_codes) {

    if (current == nullptr) {
        return;
    }

    if (!current->m_left && !current->m_right) {
        char_codes[current->m_value] = code;
    }

    generate_code(current->m_left, code + "0", char_codes);
    generate_code(current->m_right, code + "1", char_codes);
}


void huffman(std::ifstream& file) {

    std::unordered_map<unsigned char, ull> char_frequency;
    load_data(file, char_frequency);

    std::priority_queue<Node*, std::vector<Node*>, Comparator> queue;

    for (auto& node : char_frequency) {
        queue.push(create_node(node.first, node.second, nullptr, nullptr));
    }

    while (queue.size() != 1) {

        Node* left = queue.top();
        queue.pop();
        Node* right = queue.top();
        queue.pop();

        auto counter = left->m_counter + right->m_counter;
        queue.push(create_node('\0', counter, left, right));
    }
    
    std::map<unsigned char, std::string> char_codes;
    Node* root = queue.top();
    generate_code(root, "", char_codes);

    for (auto& i : char_codes) {
        std::cout << +i.first << ": " << i.second << "\n";
    }
}

The general answer is of course to use smart pointers, like std::shared_ptr<Node> .一般的答案当然是使用智能指针,比如std::shared_ptr<Node>
That said, using regular pointers is not that bad, especially if you hide all pointers from the outside.也就是说,使用常规指针并没有那么糟糕,尤其是当您从外部隐藏所有指针时。 I wouldn't agree with "you shouldn't use new ", more like "you should realize that you have to make sure not to create a memory leak if you do".我不同意“你不应该使用new ”,更像是“你应该意识到,如果你这样做,你必须确保不会造成 memory 泄漏”。

In any case, for something like you do, especially with your vector, you don't need actual pointers at all.无论如何,对于像你这样的事情,尤其是你的矢量,你根本不需要实际的指针。 Simply store an index for your vector and replace every occurence of Node* by int , somewhat like:只需为您的向量存储一个索引,并将每次出现的Node*替换为int ,有点像:

class Node
{
    public:

        // constructors and accessors

    private:

        ValueType value;
        int index_left;
        int index_right;
}

I used a signed integer as index here in order to allow storing -1 for a non-existent reference, similar to a null pointer.我在这里使用签名的 integer 作为索引,以便允许为不存在的引用存储 -1,类似于 null 指针。
Note that this only works if nothing gets erased from the vector, at least not before everything is destroyed.请注意,这仅在向量中没有任何内容被删除时才有效,至少在所有内容都被销毁之前不会。 If flexibility is the key, you need pointers of some sort.如果灵活性是关键,那么您需要某种指示。

Also note that you should not have a vector as a global variable .另请注意,您不应将矢量作为全局变量 Instead, have a wrapping class, of which Node is an inner class, somewhat like this:相反,有一个包装 class,其中Node是一个内部 class,有点像这样:

class Tree
{
    public:

        class Node
        {
        ...
        };

        // some methods here

    private:
        
        vector<Node> nodes;
}

With such an approach, you can encapsulate your Node class better.使用这种方法,您可以更好地封装您的Node class。 Tree should most likely be a friend . Tree应该最有可能成为friend Each Node would store a reference to the Tree it belongs to.每个Node都会存储对其所属Tree的引用。

Another possibility would be to make the vector a static member for Node , but I would advise against that.另一种可能性是使 vector 成为Node的 static 成员,但我建议不要这样做。 If the vector is a static member of Node or a global object, in both cases, you have all trees you create being in one big container, which means you can't free your memory from one of them when you don't need it anymore.如果向量是 static Node成员或全局 object,在这两种情况下,您创建的所有树都在一个大容器中,这意味着您不能在不需要时从其中一个容器中释放 memory了。
While this would technically not be a memory leak, in practice, it could easily work as one.虽然这在技术上不是 memory 泄漏,但实际上,它可以很容易地作为一个泄漏。
On the other hand, if it is stored as a member of a Tree object, the memory is automatically freed as soon as that object is removed.另一方面,如果它存储为Tree object 的成员,则一旦 object 被删除,memory 就会自动释放。

but I want to create nodes without using new operator because I know that you shouldn't use it.但我想在不使用 new 运算符的情况下创建节点,因为我知道你不应该使用它。

The reason it is discouraged to use new directly is that the semantics of ownership (ie who is responsible for the corresponding delete ) isn't clear.不鼓励直接使用new的原因是所有权的语义(即谁负责相应的delete )不明确。

The c++ standard library provides the Dynamic memory management utilities for this, the smart pointers in particular. c++ 标准库为此提供了动态 memory 管理实用程序,尤其是智能指针。

So I think your create function should look like follows:所以我认为您创建的 function 应该如下所示:

std::unique_ptr<Node> create_node(unsigned char value, unsigned long long counter, Node* left, Node* right) {

    std::unique_ptr<Node> temp = std::make_unique<Node>();
    temp->m_value = value;
    temp->m_counter = counter;
    temp->m_left = left;
    temp->m_right = right;

    return temp;
}

This way it's clear that the caller takes ownership of the newly created Node instance.这样很明显,调用者获得了新创建的Node实例的所有权。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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