use_count 在 C++ 中使用 shared_ptr 时变为 -1
use_count becomes -1 when using shared_ptr in C++
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) {
use_count
递增 1 p
。
所以,考虑到在 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
我收到一个错误:
Error 1 error C2664: 'GenericStack::push' : cannot convert parameter 1 from 'std::shared_ptr<_Ty>' to 'std::shared_ptr<_Ty> &' ...\stack\genericstack.h 47 Stack
关于这部分:
void push(std::shared_ptr<T>& p) {
GenericStack::push(p);
}
让我们回顾一下 specification of std::static_pointer_cast
:std::static_pointer_cast
() returns 一个右值 a.k.a。临时对象。
std::shared_ptr<T>& top() { return std::static_pointer_cast<T>(GenericStack::top()); }
此 returns 对临时对象的引用,该对象在 top()
returns 时被销毁。未定义的行为。大多数现代 C++ 编译器通常都能够检测到这种未定义行为的常见实例,并且您的编译器应该在这条线上对您咆哮。
一般来说,试图通过从 void *
(这组模板的明显潜在目的)来回转换来破坏 C++ 的类型安全——永远不会有好结果。
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) {
use_count
递增 1 p
。
所以,考虑到在 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
我收到一个错误:
Error 1 error C2664: 'GenericStack::push' : cannot convert parameter 1 from 'std::shared_ptr<_Ty>' to 'std::shared_ptr<_Ty> &' ...\stack\genericstack.h 47 Stack
关于这部分:
void push(std::shared_ptr<T>& p) {
GenericStack::push(p);
}
让我们回顾一下 specification of std::static_pointer_cast
:std::static_pointer_cast
() returns 一个右值 a.k.a。临时对象。
std::shared_ptr<T>& top() { return std::static_pointer_cast<T>(GenericStack::top()); }
此 returns 对临时对象的引用,该对象在 top()
returns 时被销毁。未定义的行为。大多数现代 C++ 编译器通常都能够检测到这种未定义行为的常见实例,并且您的编译器应该在这条线上对您咆哮。
一般来说,试图通过从 void *
(这组模板的明显潜在目的)来回转换来破坏 C++ 的类型安全——永远不会有好结果。