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

How to avoid using new operator in C++?

本文关键字:运算符 何避免 C++      更新时间:2024-09-26

我有一个C++程序,它为文件中的所有字符创建霍夫曼代码。它工作得很好,但我想在不使用新运算符的情况下创建节点,因为我知道你不应该使用它。我尝试使用向量全局变量来保存节点,但这不起作用。

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];
}

编辑:我添加了更多的代码,我没有真正解释什么不起作用。问题是在generate_code()中,它从未达到nullptr。我也尝试过使用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('', 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";
}
}

一般的答案当然是使用智能指针,比如std::shared_ptr<Node>
也就是说,使用常规指针并没有那么糟糕,尤其是当您将所有指针都隐藏在外部时。我不会同意";不应该使用CCD_ 3";,更像";你应该意识到,如果你这样做了,你必须确保不会造成内存泄漏;。

在任何情况下,对于像您这样的操作,尤其是使用向量,您根本不需要实际的指针。只需为矢量存储一个索引,并用int替换每次出现的Node*,有点像:

class Node
{
public:
// constructors and accessors
private:
ValueType value;
int index_left;
int index_right;
}

我在这里使用了一个带符号的整数作为索引,以便为不存在的引用存储-1,类似于null指针
请注意,只有在矢量中没有任何内容被擦除的情况下,这才有效,至少在所有内容都被销毁之前不会。如果灵活性是关键,你需要某种指针

还要注意,不应该将向量作为全局变量。相反,有一个包装类,其中Node是一个内部类,有点像这样:

class Tree
{
public:
class Node
{
...
};
// some methods here
private:

vector<Node> nodes;
}

使用这样的方法,可以更好地封装Node类。CCD_ 8最有可能是CCD_。每个Node将存储对其所属的Tree的引用

另一种可能性是使向量成为Node的静态成员,但我建议不要这样做。如果向量是Node的静态成员或全局对象,在这两种情况下,您创建的所有树都在一个大容器中,这意味着当您不再需要它们时,您无法从其中一个容器中释放内存
虽然这在技术上不会是内存泄漏,但在实践中,它可以很容易地作为一个内存泄漏工作
另一方面,如果它存储为Tree对象的成员,则一旦删除该对象,内存就会自动释放。

但我想在不使用新运算符的情况下创建节点,因为我知道你不应该使用它。

不鼓励直接使用new的原因是所有权的语义(即谁负责相应的delete(不清楚。

c++标准库为此提供了动态内存管理实用程序,尤其是智能指针。

所以我认为您的创建函数应该如下所示:

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;
}

通过这种方式,很明显,调用者拥有新创建的Node实例的所有权。