#include<iostream>
#include<set>
template <typename T>
/* Simple smart pointer class */
class SmartPtr
{
T *ptr;
public:
explicit SmartPtr(T *p = NULL) { ptr = p; }
~SmartPtr() { delete(ptr); }
T & operator * () { return *ptr; }
T * operator -> () { return ptr; }
};
class simple {
private:
int x;
public:
simple(int y = 0) :x(y) {}
int getX() { return x; }
};
typedef SmartPtr<simple> simplePtr;
int main() {
std::set<simplePtr> st;
simplePtr p1 = simplePtr(new simple(5));
simplePtr p2 = simplePtr(new simple(5));
simplePtr p3 = simplePtr(new simple(5));
simplePtr p4 = simplePtr(new simple(5));
std::cout << p1->getX(); <-- working fine
st.insert(p1);
st.insert(p2);
st.insert(p3);
st.insert(p4);
for (std::set<simplePtr>::iterator it = st.begin(); it != st.end(); ++it)
{
std::cout << it->getX(); // Not working??
}
}
编译失败,并在Visual Studio 2013中出现错误:
Error C2039 getX: is not a member of SmartPtr<simple>
在Linux上:
error: ‘const class SmartPtr<simple>’ has no member named ‘getX’
这是迭代器的问题吗?
最佳答案
您可以将it->getX()
视为(*it).getX()
的语法糖。 [原则上,类可以不一致地重载->
和*
(取消引用)运算符,但是std::set<T>::iterator
毫不奇怪,不会违反该约定]。因此,在您的情况下,*it
被取消引用为const SmartPtr<simple>&
类型的左值,并且.getX()
失败,因为SmartPtr
没有getX()
方法。因为,相反,您的意思是访问获得的SmartPtr
指向的对象,所以您必须再添加一级解除引用:
修正1
将it->getX()
替换为(**it).getX()
或(*it)->getX()
。
但是,还有另一个问题-*it
会导致const
SmartPtr
(是的,std::set
的非常数迭代器不提供对容器元素的写访问,否则您可能会破坏正确的顺序)容器中的元素)。但是->
中的*
和SmartPtr
(解引用)运算符都以仅在非const
对象上被调用的方式定义。要解决此问题,必须使这两个函数const
:
校正2(在SmartPtr<T>
中)
// vvvvv
T & operator * () const { return *ptr; }
T * operator -> () const { return ptr; }
// ^^^^^
进行第二次更正后,可以用range-for循环替换旧式的for循环:
for (const simplePtr& p : st)
{
std::cout << p->getX();
}
尽管如此,您的程序仍无法编译-由于无法比较
SmartPtr<T>
对象,因此无法将它们放入std::set
中。通过定义operator<()
来解决此问题:修正3
添加到
SmartPtr<T>
:bool operator<(const SmartPtr& other) const { return ptr < other.ptr; }
此时,您的代码可以编译,但是很有可能无法正常工作。原因是
SmartPtr<T>
的复制语义由编译器自行决定,无法满足您的意图。通过发现违反Rule of Three, Four and Five的方法,很容易猜到-您的类定义了析构函数,但未能定义复制和/或移动构造函数和赋值运算符。结果,您的代码将执行两次删除,因此无法保证任何明确定义的行为。校正4
修复
SmartPtr<T>
的复制语义。我通过为
SmartPtr
分配移动语义来“修复”您的代码(这需要在std::move()
-将其添加到insert()
时添加std::set
):#include<iostream>
#include<set>
template <typename T>
class SmartPtr
{
T *ptr;
public:
explicit SmartPtr(T *p = NULL) { ptr = p; }
~SmartPtr() { delete(ptr); }
SmartPtr(const SmartPtr& other) = delete;
SmartPtr(SmartPtr&& other) : ptr(other.ptr) { other.ptr = NULL; }
SmartPtr& operator=(SmartPtr other)
{
std::swap(ptr, other.ptr);
return *this;
}
T & operator * () const { return *ptr; }
T * operator -> () const { return ptr; }
bool operator<(const SmartPtr& other) const { return ptr < other.ptr; }
};
class simple {
int x;
public:
simple(int y = 0) : x(y) {}
int getX() { return x; }
};
typedef SmartPtr<simple> simplePtr;
int main() {
std::set<simplePtr> st;
simplePtr p1 = simplePtr(new simple(5));
simplePtr p2 = simplePtr(new simple(5));
st.insert(std::move(p1));
st.insert(std::move(p2));
for (const simplePtr& p : st)
{
std::cout << p->getX();
}
return 0;
}