use_count становится -1 при использовании shared_ptr в C ++ - PullRequest
0 голосов
/ 25 ноября 2018

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 увеличивается на единицу для 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

Я получаю ошибку:

Ошибка 1 ошибка C2664: «GenericStack :: push»: невозможно преобразовать параметр 1 из «std :: shared_ptr <_Ty>» в «std :: shared_ptr <_Ty> & '... \ stack \ genericstack».h 47 Stack

Именно об этой части:

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

1 Ответ

0 голосов
/ 25 ноября 2018

Давайте рассмотрим спецификацию std::static_pointer_cast: std::static_pointer_cast () возвращает rvalue, то есть временный объект.

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

Возвращает ссылку на временный объект, которыйуничтожается к тому времени, когда это top() возвращается.Неопределенное поведение.Большинство современных компиляторов C ++ обычно способны обнаруживать этот распространенный случай неопределенного поведения, и ваш компилятор должен лаять на вас, в этой строке.

В общем, попытки победить безопасность типов в C ++ путем отбрасывания вещей назади далее от void * (очевидная основная цель этого набора шаблонов) - это никогда не заканчивается хорошо.

...