取消引用不适用于集

时间:2016-11-18 13:16:31

标签: c++ smart-pointers

#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’

这是迭代器的问题吗?

3 个答案:

答案 0 :(得分:4)

您可以将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; }

body

答案 1 :(得分:2)

你的迭代器需要被解引用,此时你会得到一个指针。然后需要解除引用。所以:

    std::cout << (*it)->getX();

答案 2 :(得分:0)

对于初学者,您必须先为班级operator <定义SmartPtr,然后才能在集合中使用它。

其次,您必须声明成员函数getX,如

int getX() const { return x; }

你必须写至少像

std::cout << ( *it )->getX();