Domanda

Ho scritto un programma di test case per dimostrare un problema con un mio programma più ampio, e il caso di test ha un bug che il programma originale non ha.

Ecco il file di intestazione:

// compiled with g++ -I/usr/local/bin/boost_1_43_0 -Wall -std=c++0x -g test.cpp

#include <bitset>
#include <boost/shared_ptr.hpp>
#include <vector>

typedef std::vector< std::vector< std::bitset<11> > > FlagsVector;

namespace yarl
{
    namespace path
    {
        class Pathfinder;
    }

    namespace level
    {
        class LevelMap
        {
        // Member Variables
            private:
                int width, height;
                FlagsVector flags;

            public:
                boost::shared_ptr<path::Pathfinder> pathfinder;

        // Member Functions
            LevelMap(const int, const int);

            int getWidth() const {return width;}
            int getHeight() const {return height;}

            bool getFifthBit(const int x, const int y) const
            {
                return flags.at(x).at(y).test(5);
            }
        };



        class Level
        {
        // Member Variables
            public:
                LevelMap map;

        // Member Functions
            public:
                Level(const int w=50, const int h=50);
        };
    }


    namespace path
    {
        class Pathfinder
        {
        // Member Variables
            private:
                boost::shared_ptr<level::LevelMap> clientMap;

        // Member Functions
            public:
                Pathfinder() {}
                Pathfinder(level::LevelMap* cm)
                : clientMap(cm) {}

                void test() const;
        };
    }
}

ed ecco il file di implementazione:

#include <iostream>
#include "test.hpp"
using namespace std;

namespace yarl
{
    namespace level
    {
        LevelMap::LevelMap(const int w, const int h)
        : width(w), height(h), flags(w, vector< bitset<11> >(h, bitset<11>())),
          pathfinder(new path::Pathfinder(this)) 
        {}



        Level::Level(const int w, const int h)
        : map(w,h)
        {
            map.pathfinder->test();
        }
    }



    namespace path
    {
        void Pathfinder::test() const
        {
            int width = clientMap->getWidth();
            int height = clientMap->getHeight();
            cerr << endl;
            cerr << "clientMap->width: " << width << endl; 
            cerr << "clientMap->height: " << height << endl; 
            cerr << endl;
            for(int x=0; x<width; ++x)
            {
                for(int y=0; y<height; ++y)
                {
                    cerr << clientMap->getFifthBit(x,y);
                }
                cerr << "***" << endl; // marker for the end of a line in the output
            }
        }
    }
}

int main()
{
    yarl::level::Level l;
    l.map.pathfinder->test();
}

Collego questo programma con la recinzione elettrica, e quando lo eseguo si interrompe con questo errore:

ElectricFence Aborting: free(bffff434): address not from malloc().

Program received signal SIGILL, Illegal instruction.
0x0012d422 in __kernel_vsyscall ()

il backtracing da gdb mostra che l'istruzione illegale si trova nel distruttore generato dal compilatore di Pathfinder, che sta avendo problemi a distruggere il suo shared_ptr.Qualcuno ha capito perche'?

È stato utile?

Soluzione

yarl::level::Level l;
.

Si istanziare una variabile automatica Level, che, nel suo costruttore costruisce il suo membro pathfinder come:

pathfinder(new path::Pathfinder(this))
.

Poi nel costruttore Pathfinder, prende il puntatore Level che si passa e assegna a un shared_ptr. Il shared_ptr prende quindi la proprietà di questo puntatore.

Questo non è corretto per diversi motivi:

    .
  1. Un shared_ptr deve essere utilizzato per gestire oggetti assegnati dinamicamente, non oggetti automaticamente assegnati
  2. Se si desidera utilizzare shared_ptr, dovresti usarlo ovunque: come ora, passano i puntatori grezzi (ad esempio al costruttore di Pathfinder, ma quindi archiviali come shared_ptrs. Questo apre una grande lattina di vermi di proprietà. .
  3. Il modo corretto per assegnare this a un shared_ptr è quello di derivare da enable_shared_from_this; Nota tuttavia che non è possibile ottenere un shared_ptr da this in un costruttore.
  4. Quando il shared_ptr viene distrutto, proverà a eliminare il puntatore che riesce. In questo caso, tuttavia, quel puntatore non è a un oggetto assegnato dinamicamente (cioè, assegnato con new), ma a un oggetto assegnato automaticamente (cioè, sullo stack). Quindi, l'errore.

    Se non hai bisogno di qualcosa per prendere la proprietà di una risorsa, non c'è niente di sbagliato nell'utilizzo di un puntatore grezzo (o di un riferimento, se hai questa opzione).

Altri suggerimenti

Stai costruendo shared_ptr da un puntatore che non dovrebbe essere gestito da shared_ptr.(Il puntatore this)

Quando l'ultima copia di Shared_ptr viene distrutta questa memoria è libera - Quando infatti non dovrebbe - il this è in serie in questo caso.

C'è una ragione per cui il costruttore di shared_ptr è esplicito - è esattamente per evitare una conversione inosservato come una conversione inosservato dal puntatore regolare, che non deve essere gestito da shared_ptr, a uno shared_ptr - una volta che si passa un tale puntatore in Shared_ptr, il tuoIl programma è condannato: l'unica via d'uscita è eliminando il puntatore che non hai intenzione di eliminare.

In generale è consigliabile costruire il puntatore condiviso con nuovo direttamente - come ptr(new Somethings(x,y,z) - in questo modo non rischi di un'eccezione che perde il tuo assegnato, ma non assegnato a una memoria shared_ptr.

Il Level contiene un LevelMap variabile membro.Quando il Level viene distrutto, distruggerà anche il suo LevelMap.

D'altra parte un puntatore a questo LevelMap il membro è passato a Pathfinder, che crea un shared_ptr<> dal puntatore passato.Questo appena creato shared_ptr<> pensa di possedere l'oggetto a cui punta e cercherà di distruggerlo una volta che il Pathfinder viene distrutto.

Quindi il LevelMap viene distrutto più volte.

Nell'esempio il LevelMap viene creato in pila.Pertanto il delete chiamato da shared_ptr<> può vedere che l'indirizzo non è dall'heap e si ottiene un errore.Se anche il tuo vero programma ha questo problema, ma tutti quegli oggetti sono allocati dinamicamente, l'errore probabilmente non verrà rilevato.Si otterrà solo la corruzione della memoria silenziosa e strani arresti anomali in seguito.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top