对于C ++ 11及以上版本:
ad_tree_node
是指Union
数据结构,旨在保存类型为ad_node
的{{1}}或vary_node
类型的对象。
代码编译但与struct
一起使用时会检测到内存泄漏。
以下是代码:
valgrind
使用以下标志编译:
struct ad_node {
std::string tag_;
int count_;
std::vector<int> index_;
bool leaf_;
};
struct vary_node {
std::string tag_;
int index_;
int mcv_;
};
union ad_tree_node {
ad_node ad;
vary_node vy;
ad_tree_node () { std::memset(this, 0, sizeof(ad_node)); }
~ad_tree_node() {}
};
std::string print_ad_tree_node(const ad_tree_node& nd, bool is_ad_node) {
std::string st = "";
if (is_ad_node) {
st += "ad_node: " + nd.ad.tag_+ ", " + std::to_string(nd.ad.count_) + ", ";
if (nd.ad.leaf_) { st += " leaf, "; }
else { st += "non-leaf, "; }
st += "index: ";
std::cout << nd.ad.index_.size();
for (auto x : nd.ad.index_) { st += std::to_string(x) + ", "; }
}
else {
st += "vy_node: " + nd.vy.tag_ + ", mcv_: " + std::to_string(nd.vy.mcv_) + ", ";
st += "index: ";
std::cout << nd.vy.index_;
}
return st;
}
int main () {
ad_node t1;
for (int i = 0; i < 10; ++i) t1.index_.push_back(i);
t1.tag_ = "A";
t1.leaf_ = true;
t1.count_ = 9;
ad_tree_node nd;
nd.ad.tag_ = t1.tag_;
nd.ad.leaf_ = t1.leaf_;
nd.ad.count_ = t1.count_;
nd.ad.index_ = std::move(t1.index_);
std::cout << print_ad_tree_node(nd, true) << std::endl;
return 0;
}
g++ -std=c++11 -g3 code.cpp
valgrind --leak-check=full ./a.out
报告的泄漏
valgrind:
答案 0 :(得分:4)
ad_tree_node
的析构函数只是{}
。与普通类类型不同,其析构函数将自动为每个基类和每个成员对象调用析构函数,对于联合,您必须自己完成所有操作。在这种情况下,~ad_tree_node()
不会调用~ad_node()
,因此std::string
和std::vector
成员分配的所有内存都会泄露。这正是valgrind所抱怨的。
您还遇到memset
的问题 - 这与非标准布局类型无法很好地协作。 memset
- string
或vector
是UB。
你真正想要的是一个变体:
using ad_tree_node = variant<ad_node, vary_node>;
用C ++的说法,variant
就像union
- 除了它知道它包含哪种类型,按照C ++对象语义正确管理其存储,并自行清理。对于C ++ 11,一个很好的实现是Boost.Variant。