【问题标题】:use_count becomes -1 when using shared_ptr in C++在 C++ 中使用 shared_ptr 时,use_count 变为 -1
【发布时间】:2018-11-25 12:31:21
【问题描述】:

GenericStack.h

#ifndef _GENERIC_STACK_TROFIMOV_H_
#define _GENERIC_STACK_TROFIMOV_H_

#include <memory>

class GenericStack {
    struct StackNode {
        std::shared_ptr<void> _data; 
        StackNode* _next;
        StackNode(const std::shared_ptr<void>& p, StackNode* next) 
            : _data(p), _next(next) {

        }
    };
    StackNode* _top; 

    GenericStack(const GenericStack&);
    GenericStack& operator=(const GenericStack&);

protected:
    GenericStack();
    ~GenericStack();
    void push(const std::shared_ptr<void>&);
    void pop();
    std::shared_ptr<void>& top();
    bool isEmpty() const;

public:
    class EmptyError {
        const char* _message;
    public:
        EmptyError(const char* message)
            : _message(message) {

        }
        const char* getMessage() const {
            return _message;
        }
    };
};

template <class T>
class TStack: private GenericStack {                  
public:
    void push(const std::shared_ptr<T>& p) { GenericStack::push(p); }
    void pop() { GenericStack::pop(); }
    std::shared_ptr<T>& top() { return std::static_pointer_cast<T>(GenericStack::top()); }
    bool isEmpty() const { return GenericStack::isEmpty(); }
};

#endif

GenerickStack.cpp

#include "GenericStack.h"

GenericStack::GenericStack()
    : _top(0) {

};
GenericStack::~GenericStack() {
    while(!isEmpty()) {
        pop();
    }
};

void GenericStack::push(const std::shared_ptr<void>& p) {
    _top = new StackNode(p, _top);
}

std::shared_ptr<void>& GenericStack::top() {
    if(isEmpty()) {
        throw EmptyError("No more elements in stack.");
    }
    return _top->_data;
}
void GenericStack::pop() {
    if(isEmpty()) {
        throw EmptyError("No more elements in stack.");
    }

    StackNode* t = _top->_next;
    delete _top;
    _top = t;
}

bool GenericStack::isEmpty() const {
    return !_top;
}

Main.cpp

#include <iostream>
#include "GenericStack.h"
//#define NDEBUG
#include <assert.h>

void ordinaryUsageVerification() {
    TStack<int> intStack;

    {
        std::shared_ptr<int> sh(new int(7));
        intStack.push(sh);
        intStack.isEmpty();
        assert(!intStack.isEmpty() && sh.use_count() == 2);
    }
    //assert(!intStack.isEmpty() && intStack.top().use_count() == 1);
    std::cout << "intStack.top().use_count(): " << intStack.top().use_count() << std::endl;

    std::cout << "*gs.top(): " << *intStack.top() << std::endl;
    intStack.pop();
    assert(intStack.isEmpty());
}


int main() {
    ordinaryUsageVerification();

    return 0;
}

Main.cpp中以下两行之后:

std::shared_ptr<int> sh(new int(7));
intStack.push(sh);

我希望intStack.top().use_count() 等于 2,但它等于 -1。

我期待这样的行为,因为在调用 push 方法时,我通过引用传递了 shared_ptr,所以 use_count 不应该改变。并且仅在 GenericaStack.h 的一处:

StackNode(const std::shared_ptr<void>& p, StackNode* next) 
            : _data(p), _next(next) {

对于puse_count 加一。

所以,鉴于在push 之前我有sh.use_count() == 1,在intStack.push(sh); 之后我有sh.use_count() == 2 我应该得到intStack.top().use_count() == 2,但我得到的是intStack.top().use_count() == -1。为什么?

谢谢。

这样修改GenericStack.h后:

#ifndef _GENERIC_STACK_TROFIMOV_H_
#define _GENERIC_STACK_TROFIMOV_H_

#include <memory>

class GenericStack {
    struct StackNode {
        std::shared_ptr<void> _data; 
        StackNode* _next;
        StackNode(std::shared_ptr<void>& p, StackNode* next) 
            : _data(p), _next(next) {

        }
    };
    StackNode* _top; 

    GenericStack(const GenericStack&);
    GenericStack& operator=(const GenericStack&);

protected:
    GenericStack();
    ~GenericStack();
    void push(std::shared_ptr<void>&);
    void pop();
    std::shared_ptr<void>& top();
    bool isEmpty() const;

public:
    class EmptyError {
        const char* _message;
    public:
        EmptyError(const char* message)
            : _message(message) {

        }
        const char* getMessage() const {
            return _message;
        }
    };
};

template <class T>
class TStack: private GenericStack {                  
public:
    void push(std::shared_ptr<T>& p) { 
        GenericStack::push(p); 
    }
    void pop() { GenericStack::pop(); }
    std::shared_ptr<T> top() { return std::static_pointer_cast<T>(GenericStack::top()); }
    bool isEmpty() const { return GenericStack::isEmpty(); }
};

#endif

我收到一个错误:

错误 1 ​​错误 C2664: 'GenericStack::push' : 无法将参数 1 从 'std::shared_ptr<_ty>' 转换为 'std::shared_ptr<_ty> &' ...\stack\genericstack.h 47堆栈

关于这部分:

void push(std::shared_ptr<T>& p) { 
    GenericStack::push(p); 
}

【问题讨论】:

标签: c++ pointers memory shared-ptr


【解决方案1】:

让我们回顾一下specification of std::static_pointer_caststd::static_pointer_cast() 返回一个右值,也就是一个临时对象。

std::shared_ptr<T>& top() { return std::static_pointer_cast<T>(GenericStack::top()); }

这将返回一个临时对象的引用,该对象在 top() 返回时被销毁。未定义的行为。大多数现代 C++ 编译器通常能够检测到这种未定义行为的常见实例,并且您的编译器应该在这一行上向您咆哮。

一般来说,试图通过在void *(这组模板的明显潜在目的)中来回转换东西来破坏 C++ 的类型安全——这永远不会结束。

【讨论】:

  • 这不是“击败类型安全”。使用此堆栈的代码将始终返回他们放入其中的类型。这并不意味着实际的堆栈逻辑需要在类型上进行模板化。我们一直使用类型擦除来确保只有真正强制的类型信息是通用的。
  • 至于返回类型,左值引用可以绑定到右值的唯一方法是在 MSVC 及其该死的扩展中。值得一提的是。
  • 临时文件不绑定到可变引用,只绑定到const引用,这就是推送错误的原因。
猜你喜欢
  • 1970-01-01
  • 2018-07-30
  • 1970-01-01
  • 1970-01-01
  • 2022-01-03
  • 1970-01-01
  • 2020-10-08
  • 2020-01-11
  • 2012-04-15
相关资源
最近更新 更多