【发布时间】:2020-03-19 13:45:52
【问题描述】:
所以这是我的第一个问题。我已经搜索了该网站,找到了一些东西并应用了其中给出的建议,但我仍然不确定我是否以正确的方式做到了。
我正在开发一个模板库,这是我对 BST 类模板的实现:
template <class T>
class bstree
{
private:
struct bstnode
{
bstnode* pRight; //node to the right (greater)
bstnode* pLeft; //node to the left (lesser)
bstnode* pParent; //parent node
T mValue; //contents
};
class bstnodeiterator : public _iterator_base<T, bstree<T>>
{
public:
bstnodeiterator(bstnode* pNode = nullptr, bstree<T> pCont = nullptr)
: _mpNodePtr(pNode), _mpCont(pCont) {}
//functions from _iterator_base<>
bool is_null() const { return (_mpNodePtr == nullptr); }
const bstree<T>* get_container() const { return this->_mpCont; }
//get_pointer() is intentionally not defined.
//operators (e.g. increment, decrement, advance by, dereference, etc)
//go here!
//...
private:
friend class bstree<T>;
//member elements:
bstree<T>* _mpCont; //the container that the iterator is created by
bstnode* _mpNodePtr; //the actual pointer pointing to the bst-node of '_mpCont'
};
public:
using val = T;
using val_ref = T&;
using val_ptr = T*;
using iter = bstnodeiterator;
public:
iter begin() const;
iter end() const;
//other public member functions (e.g. insert(), remove(), etc.) go here!
//...
private:
bstnode* _mpRoot; //The root node of the BST
size_t _mSize; //The number of elements in the container (guaranteed O(1))
};
bstnodeiterator::get_container() 和 bstnodeiterator::is_null() 派生自 iterator_base<>,它是所有其他容器(例如 vector<>、fixed_list<>、map<> 等)的迭代器的基类:
template <class T, class Cont>
struct _iterator_base
{
virtual bool is_null() const = 0;
virtual const Cont* get_container() const = 0;
/*virtual*/ const T* get_pointer() const /* = 0*/;
};
//is_null() and get_container() should be defined in derived classes
//because they are used everywhere in the library!
- 需要定义以上所有三个函数,因为它们在整个库中的其他任何地方都使用(例如,在算法中,
iterator_helper类等)。
由于 BST 是已排序元素的容器,因此不应动态更改节点的内容。因为这会破坏树的排序结构。 因此,我想阻止程序员使用get_pointer()。 即使它返回一个指向内容的 const 指针,它仍然可以通过T 的成员函数进行更改(例如,如果@987654335 @ 是 std::string 然后可以通过 std::string::assign() 更改内容),我不想要这个。
所以,我在基类中将函数 _iterator_base<*,*>::get_pointer() 设为非虚拟。而且它没有在派生类bstnodeiterator 中定义。所以,如果程序员从派生类中调用它...
bstree<std::string> strTree = { "a string", "another string", "yet another string", "test string" };
//inserted some other elements
bstree<std::string>::iterator it = strTree.begin();
//*it = "something else"; --> this won't work, because read-only dereferencing is allowed in the class.
it.get_pointer()->assign("something else"); //this will break the tree.
...那么编译器会给出链接错误:unresolved external symbol " ... ::get_pointer()".
这是正确的方法吗?你怎么看?
编辑:
我刚刚尝试取消引用和修改:
bstree<std::string> strTree =
{
"a string",
"another string",
"yet another string",
"test string"
};
bstree<std::string>::iter it = strTree.begin();
(*it).assign("modified string"); // ----> error!
std::string pB0 = strTree.begin(); // ----> error
const std::string pB = strTree.begin();
pB->assign("modified string"); // ----> error!
...它没有编译。但是,如果我将最后一行更改为:
it.get_pointer()->assign("modified string");
...它可以无错误地编译、运行和工作!
编辑 2:
我终于找到了问题的根源:typedefs。
我没有在原始问题中显示typedefs,以使其看起来更简单、更易于阅读。在原始代码中,bstree<> 范围内有一个using val_ptr = T*;,而我在bstnodeiterator 范围内使用这个typedef:
template <class T>
class bstree
{
public:
using val = T;
using val_ref = T&;
using val_ptr = T*;
private:
class bstnodeiterator : public _iterator_base<T, bstree<T>>
{
//c'tor comes here!
const val_ptr get_pointer() { return (_mPtr ? &_mPtr->_mVal : nullptr); }
//...
};
//...
};
如果我定义了上面给出的函数,那么我可以从get_pointer() 的返回指针调用std::string::assign()。但是,如果我将函数的返回类型更改为const val*,则无法调用string::assign()。
我终于意识到这两种类型是不同的。可能编译器会将const 放在其他地方。
【问题讨论】:
-
Even if it returns a const pointer to the contents it can still be changed via member functions of T如果返回指向 const 的指针,则无法更改对象。 -
我不确定我是否理解正确,但是
const std::string的内容不能更改,这就是const关键字的全部意义所在。您不能在const对象上调用任何非const方法。 -
std::string::assign没有任何 const 限定的覆盖。您不能在任何 const 字符串上调用它。 Demo -
@KaenbyouRin 我刚刚尝试使用 bstree::begin() 访问树的第一个节点,然后调用 bstnodeiterator::get_pointer() 返回 const ptr 然后调用 std::string::分配()。
rStdStrTree.begin().get_pointer()->assign("newly assigned string");并且内容变了。我做错了什么? -
@tkausl 我知道,但我认为我做错了什么,因为正如我在之前的评论中所说,我可以更改节点的内容。
标签: c++ inheritance polymorphism virtual-functions pure-function