在尝试使用C ++ 11创建二进制搜索树(BST)时,我遇到了麻烦。 我无法从创建它的函数中提取正确创建的BST。 该函数从文件中读取数据(在这种情况下,每行只是一个数字)并从中构建BST。循环中的构建部分正常工作。 问题是我无法将临时对象移动到我想要的位置。
放大我认为的问题,请参阅编辑3.
更多背景信息:
BST<T>
是一个从std::unique_ptr<BSTknoop<T>>
公开派生的类
BST<T>
还继承了unique_ptr<BSTknoop<T>>
与
template<class T>
using BSTknoopptr=std::unique_ptr<BSTknoop<T>>; // alias template
和
using BSTknoopptr<T>::BSTknoopptr; // in the body of the BST<T> class declaration`
BSTknoop<T>
是一个包含3个字段的数据结构
一个T对象来保存节点数据
左右两个BST<T>
个对象(每个孩子本身就是一棵树)
目标是将我新创建的BST移动到调用对象中。我的想法是,在main中你可以调用BST<int> tree; tree.lees(ifs);
(ifs
一个打开的输入文件流),然后树会保存正确填充的BST。
功能:
template<class T>
istream& BST<T>::lees(istream& is){
string lijn;
T knoopwaarde;
getline(is,lijn);
knoopwaarde = stoi(lijn);
BST<T> temptree(new BSTknoop<T>());
temptree->sl = knoopwaarde;
for(int i=1; i<DATA_SET_LENGTH ; i++){
getline(is,lijn);
knoopwaarde=stoi(lijn);
temptree.add(knoopwaarde);
cout<<temptree;
}
this->swap(temptree); /* This does not work */
/* *this = move(temptree); this does not work either*/
return is;
}
我还尝试从函数返回BST<T>
并将结果移到main中。这也不起作用。
程序在运行时因未知信号而崩溃。
旁注:我不确定为什么BST<T> temptree(new BSTknoop<T>());
在模板方面有效。由于BST<T>
继承了unique_ptr<BSTknoop<T>>
编辑1:BST<T>
类的声明:
template <class T>
class BST:public BSTknoopptr<T>{
using BSTknoopptr<T>::BSTknoopptr;
public:
friend istream& operator>>(istream& is, BST<T>& bb){
return bb.lees(is);
}
friend ostream& operator<<(ostream& os, const BST<T>& bb){
//return bb.schrijflevelorder(os);
return bb.schrijfKnoop(os);
}
void add(const T&);
ostream& schrijf(ostream&);
ostream& schrijfKnoop(ostream&) const;
int aantalSleutels() const;
istream& lees(istream&);
ostream& schrijflevelorder(ostream& os) const;
private:
};
BSTknoop<T>
类的声明:
template <class T>
class BSTknoop{
friend class BST<T>;
public:
BSTknoop() {}
explicit BSTknoop(T _sl) : sl(_sl) {}
private:
T sl;
BST<T> links,rechts;
};
编辑2:我已经写了一个移动构造函数并移动赋值运算符。我已确保使用BST<T>()=default;
保留默认构造函数但我很困惑:BST类没有任何成员我必须实现自己的移动构造/操作员。
但是,BST从unique_ptr<BSTknoop<T>>
继承了如此含蓄,它必须拥有该类型的成员。假设我想保留继承,有没有任何巧妙的方法来使这项工作?
我也不能(或者不应该)实现复制构造/操作符,因为unique_ptr会删除它们吗?
template<class T>
BST<T>::BST(BST<T>&& other){
*this = move(other);
}
template<class T>
BST<T>& BST<T>::operator=(BST<T>&& other){
cout<<"called move operator BST"<<endl;
(*this).BSTknoopptr<T>::operator=(move(other));
return *this;
}
编辑3:使用我自己的移动constr / operator,temptree不再被正确填充。下面是我的add代码,用于构建树。如果我省略了我自己的移动constr / operator,那么这是有效的。我需要的是一个像this->get()->links = move(tmp)
一样的操作。
类型this->get()->links => BST<T>
tmp => BST<T>
怎么运作?它完成了我需要的操作,为什么我不能通过自己编写操作来使用与*this = move(temptree)
类似的东西。
template<class T>
void BST<T>::add(const T& value){
if(value <= this->get()->sl){
if(this->get()->links != nullptr){
this->get()->links.add(value);
}
else {
BST<T> tmp(new BSTknoop<T>(value));
this->get()->links = move(tmp);
}
}
else{
if(this->get()->rechts != nullptr){
this->get()->rechts.add(value);
}
else{
BST<T> tmp(new BSTknoop<T>(value));
this->get()->rechts = move(tmp);
}
}
}
答案 0 :(得分:0)
好吧,我犯了最愚蠢的错误。继承和伴随的移动语义没有任何问题。我在istream& BST<T>::lees(istream& is)
函数中使用了DATA_SET_LENGTH预处理程序指令。虽然我的最终数据集将包含一百万个项目。我用于测试目的的数据集只有12个。我忘了更改DATA_SET_LENGTH的值,因此stoi(lijn)
崩溃了。
对于对解决方案感兴趣的任何人:
备注强>
BST<T>& operator=(BSTknoopptr<T>&&);
并且它有效。然而,即使没有明确地编写自己的,它也可以。最后的问题(也许有人可以评论):
继承的运算符operator=(BSTknoopptr<T>&&)
,它的签名在BST<T>
中是什么样的?主要是关于返回类型(如果它现在是BST<T>
的成员,它返回什么类型?)。
** TLDR:为什么我可以依赖unique_ptr<BSTknoop<T>>
的继承移动运算符/移动构造函数? **
<强> bst.h 强>
#define DATA_SET_LENGTH 12
using namespace std;
template <class T>
class BST;
template <class T>
class BSTknoop;
template<class T>
using BSTknoopptr=std::unique_ptr<BSTknoop<T>>;
template <class T>
class BST:public BSTknoopptr<T>{
using BSTknoopptr<T>::BSTknoopptr;
public:
BST<T>& operator=(BST<T>&&) = default;
BST<T>(BST<T>&&) = default;
BST<T>()=default;
//BST<T>& operator=(BSTknoopptr<T>&&);
friend istream& operator>>(istream& is, BST<T>& bb){
return bb.lees(is);
}
friend ostream& operator<<(ostream& os, const BST<T>& bb){
//return bb.schrijflevelorder(os);
return bb.schrijfKnoop(os);
}
void add(const T&);
//schrijf schrijft uit in een vorm die min of meer menselijk leesbaar is
ostream& schrijf(ostream&);
ostream& schrijfKnoop(ostream&) const;
int aantalSleutels() const;
istream& lees(istream&);
//schrijflevelorder schrijft uit in een vorm die door lees(...) kan gelezen worden.
ostream& schrijflevelorder(ostream& os) const;
private:
};
template <class T>
class BSTknoop{
friend class BST<T>;
public:
BSTknoop() {}
explicit BSTknoop(T _sl) : sl(_sl) {}
private:
T sl;
BST<T> links,rechts;
};
//template<class T>
//BST<T>& BST<T>::operator=(BSTknoopptr<T>&& other){
// cout<<"called BST<T> move with BSTknoopptr r-value ref argument"<<endl;
// (*this).BSTknoopptr<T>::operator=(move(other));
// return *this;
//}
template<class T>
void BST<T>::add(const T& value){
if(value <= this->get()->sl){
if(this->get()->links != nullptr){
this->get()->links.add(value);
}
else {
//BSTknoopptr<T> tmp(new BSTknoop<T>(value)); if used with my own BST<T>& BST<T>::operator=(BSTknoopptr<T>&& other)
BST<T> tmp(new BSTknoop<T>(value));
this->get()->links = move(tmp);
}
}
else{
if(this->get()->rechts != nullptr){
this->get()->rechts.add(value);
}
else{
//BSTknoopptr<T> tmp(new BSTknoop<T>(value)); if used with my own BST<T>& BST<T>::operator=(BSTknoopptr<T>&& other)
BST<T> tmp(new BSTknoop<T>(value));
this->get()->rechts = move(tmp);
}
}
}
template<class T>
istream& BST<T>::lees(istream& is){
string lijn;
T knoopwaarde;
getline(is,lijn);
knoopwaarde = stoi(lijn);
BST<T> temptree(new BSTknoop<T>());
temptree->sl = knoopwaarde;
for(int i=1; i<DATA_SET_LENGTH ; i++){
getline(is,lijn);
knoopwaarde=stoi(lijn);
temptree.add(knoopwaarde);
cout<<temptree;
}
*this = move(temptree);
return is;
}
<强>的main.cpp 强>
int main(){
ifstream ifs;
ifs.open("c.dat");
if(ifs.is_open()){
BST<int> tree;
tree.lees(ifs);
tree.schrijfKnoop(cout);
}
else {
cerr<<"failed to open file"<<endl;
return -1;
}
}