tutorials.de Buch-Aktion 05/2012
ERLEDIGT
NEIN
ANTWORTEN
4
ZUGRIFFE
1068
EMPFEHLEN
  • An Twitter übertragen
  • An Facebook übertragen
AUF DIESES THEMA
ANTWORTEN
  1. #1
    Avatar von FBIagent
    FBIagent FBIagent ist offline Mitglied Brokat
    Registriert seit
    Jan 2005
    Beiträge
    281
    Guten Tag,

    ich habe einige meiner alten Klassen herausgekramt und wollte diese nun ein wenig
    verändern. Nur leider scheine ich bei der Speicherverwaltung etwas falsch zu machen.

    Ich bekomme einen access violation beim cleanup, wobei der debugger mir sagt,
    das alle Zeiger gültig sind.

    Wäre schön wenn jemand einen kurzen blick darauf werfen kann.

    SqlStatement.hpp
    Code cpp:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    
    class SQLPP_API SqlStatement {
    public:
        /*
         * Description:
         *    Created the statement
         *    No nullpointer check
         *
         * Exception on error:
         *    std::exception
         *
         * Return:
         *    no
         */
        SqlStatement( const char *Query );
        /*
         * Description:
         *    Cleans the statement
         *
         * Exception on error:
         *    no
         *
         * Return:
         *    no
         */
        virtual ~SqlStatement();
     
        /*
         * Description:
         *    Replaces the first occurence of "?" in m_Query with Number
         *
         * Exception on error:
         *    std::exception
         *
         * Return:
         *    no
         */
        void AddNumber( int Number );
        /*
         * Description:
         *    Replaces the first occurence of "?" in m_Query with Str
         *    No nullpointer check
         *
         * Exception on error:
         *    std::exception
         *
         * Return:
         *    no
         */
        void AddStr( const char *Str );
     
        /*
         * Description:
         *    Returns m_Query
         *
         * Exception on error:
         *    no
         *
         * Return:
         *    m_Query
         */
        const char *GetQuery() const;
    private:
        char *m_Query;
    };

    SqlStatement.cpp
    Code cpp:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    
    SqlStatement::SqlStatement( const char *Query )
    : m_Query( CStr::Copy( Query ) ) {
        if ( m_Query == 0 ) {
            throw std::exception( "SQL++ Exception: Insufficient memory" );
        }
    }
     
    SqlStatement::~SqlStatement() {
        delete[] m_Query;
    }
     
    void SqlStatement::AddNumber( int Number ) {
        char *NumberStr = CStr::FromInt( Number );
        char *NewQuery = CStr::ReplaceOnce( m_Query, "?", NumberStr );
     
        if ( NewQuery == 0 ) {
            throw std::exception( "SQL++ Exception: Insufficient memory" );
        }
     
        delete[] NumberStr;
        delete[] m_Query;
        m_Query = NewQuery;
    }
     
    void SqlStatement::AddStr( const char *Str ) {
        char *NewQuery = CStr::ReplaceOnce( m_Query, "?", Str );
     
        if ( NewQuery == 0 ) {
            throw std::exception( "SQL++ Exception: Insufficient memory" );
        }
     
        delete[] m_Query;
        m_Query = NewQuery;
    }
     
    const char *SqlStatement::GetQuery() const {
        return m_Query;
    }

    Diese Klasse in eine DLL exportiert. Desweiteren benutzt sie:
    Code cpp:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    
        char *Copy( const char *Str ) {
            char *NewStr = new char[ strlen( Str ) + 1 ];
     
            if ( NewStr == 0 ) {
                return 0;
            }
     
            strcpy( NewStr, Str );
            return NewStr;
        }
     
        char *ReplaceOnce( char *Str, const char *SearchStr, const char *Replacement ) {
            size_t StrLen = strlen( Str );
            size_t SearchLen = strlen( SearchStr );
            size_t ReplaceLen = strlen( Replacement );
     
            for ( size_t i = 0;i < StrLen;++ i ) {
                if ( i + SearchLen > StrLen ) {
                    return Copy( Str );
                }
     
                if ( StartsWith( Str + i, SearchStr ) ) {
                    char *NewStr = new char[ StrLen - SearchLen + ReplaceLen ];
     
                    if ( NewStr == 0 ) {
                        return 0;
                    }
     
                    strncpy( NewStr, Str, i );
                    NewStr[ i ] = '\0';
                    strcat( NewStr, Replacement );
                    strcat( NewStr, Str + i + SearchLen );
                    return NewStr;
                }
            }
     
            return Copy( Str );
        }

    Nun linke ich in einem Testprojekt meine .lib und benutze die Klasse folgendermaßen:
    Code cpp:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    int main( int argc, char *argv[] ) {
        try {
    /*        MySqlConnection Connection( "127.0.0.1", "", "", "", 3306 );*/
            SqlStatement Statement( "SELECT * FROM accounts WHERE login='?'" );
            Statement.AddStr( "fbiagent" );
            std::cout << "Query: " << Statement.GetQuery() << std::endl << std::endl;
            /*SqlResult *Result = Connection.ExecuteQuery( &Statement );*/
     
    /*        try {
                while ( Result->Next() ) {
                    std::cout << "---- " << Result->GetValue( "login" ) << " ----" << std::endl;
                    std::cout << "Password: " << Result->GetValue( "password" ) << std::endl;
                    std::cout << "Last Active: " << Result->GetValue( "lastactive" ) << std::endl;
                    std::cout << "Access Level: " << Result->GetValue( "accessLevel" ) << std::endl;
                    std::cout << "Last IP: " << Result->GetValue( "lastIP" ) << std::endl;
                    std::cout << "Last Server: " << Result->GetValue( "lastServer" ) << std::endl << std::endl;
                }
            } catch( std::exception &e ) { // no such column
                std::cout << e.what() << std::endl;
            }*/
     
            /*delete Result;*/
        } catch ( std::exception &e ) { // connect failed, insufficient mem, query error
            std::cout << e.what() << std::endl;
        }
    }

    Wenn das Ende des Gültigkeitsbereiches von Statement erreicht ist am Ende des try
    Blocks wird mir angezeigt, dass eine HEAP CORRUPTION vorliegt und ich versuche
    Speicher freizugeben der mir nicht gehört. Der Debugger zeigt nun aber an, dass
    alle Zeiger gültig sind. Nun verstehe ich nicht was ich falsch gemacht habe.

    Best wishes
    FBIagent
     
    Don't blame people for bugs. Work together to make things better. No
    finger pointing! Not ever! A good rule is to Never Assume An Attack.
    If you find yourself getting angry, assume it's a misunderstanding, not an
    attack.

  2. #2
    Avatar von Endurion
    Endurion Endurion ist offline Mitglied Diamant
    Registriert seit
    Apr 2004
    Beiträge
    2.151
    Das unten aufgeführte trifft nur zu, wenn du tatsächlich eine DLL erstellst und die .lib nur auf die DLL verweist:

    Bei DLL-übergreifender Speicherbenutzung (das ist auch die einzige Entschuldigung, warum nicht std::string verwendet wird) musst du aufpassen.
    Wenn du nicht auf DLL-Runtime umgestellt hast (für die DLL und das Hauptprojekt), bekommst du Probleme, wenn du new in der einen Umgebung und das delete in der anderen machst.

    Dann hast du zwei Heaps, die sich gegenseitig nicht kennen. Wenn du dann in einem der beiden das new ausführst, und im anderen das delete, dann beschwert sich der, weil er die Speicheralloziierung in seiner Liste nicht findet.

    Da gibt es zwei Möglichkeiten:

    1) Auf DLL-Runtime umstellen. Dann benutzen beide den gleichen Heap und das Problem erledigt sich. Hat den Nachteil, dass du jetzt eine zusätzliche DLL mit ausliefern/installieren musst. Und sowas ist grundsätzlich sch***e.

    2) Über ein Interface arbeiten, das die SQLStatement-Klasse wegkapselt. Mit anderen Worten, dein Hauptprogramm hat keinen Code von der SQLStatement-Klasse in der Hand (was ja das eigentliche Problem ist). Hat den Nachteil, dass du da ein Interface dazwischenklemmen musst. Und eine Create/Destroy-Funktion, die dir eine SQLStatement-Klasse in der DLL erzeugt und das Interface zurückgibt.
     

  3. #3
    Avatar von FBIagent
    FBIagent FBIagent ist offline Mitglied Brokat
    Registriert seit
    Jan 2005
    Beiträge
    281
    Wie du schon richtig erahnt hast, sind die runtime libraries statisch gelinkt.
    Also in MSVC++ in den Projekt optionen unter C++->Code Generation
    von Multithreaded DLL auf Multithreaded und von Multithreaded Debug DLL auf
    Multithreaded Debug gestellt. Bei der DLL und dem Testprojekt.

    Das mit den zwei Heaps wusste ich bis jetzt noch nicht.

    Ich hatte auch nocht etwas vergessen, undzwar gibt es den Zugriffsfehler beim freigeben
    von m_Query im D'tor, nicht beim freigeben von SqlStatement selbst.

    Der C'tor von SqlStatement allokiert speicher für m_Query auf dem DLL-Heap und
    der D'tor versucht diesen auf dem Anwendungs-Heap zu zerstören?

    In wie fern Kapseln? Eine klasse von wegen SqlStatementWrapper die einen Member
    hat vom typ SqlStatement als Zeiger. Im C'tor beispielsweise eine exportierte Funktion
    aufrufen beispielsweise GetNewSqlStatement() die einen Zeiger auf ein neues
    SqlStatement Objekt zurückgibt und im D'tor eine exportierte Funktion beispielsweise
    DeleteSqlStatement() die als parameter einen pointer zu einem SqlStatement nimmt
    die der C'tor mit seinem SqlStatement Zeiger aufruft?

    Ein kleines Beispiel wäre nicht schlecht da ich so noch nicht gearbeitet habe.

    Best wishes
    FBIagent
     
    Don't blame people for bugs. Work together to make things better. No
    finger pointing! Not ever! A good rule is to Never Assume An Attack.
    If you find yourself getting angry, assume it's a misunderstanding, not an
    attack.

  4. #4
    Avatar von Endurion
    Endurion Endurion ist offline Mitglied Diamant
    Registriert seit
    Apr 2004
    Beiträge
    2.151
    Das ist nicht weltbewegend schön, aber so würde es gehen:

    Du machst dir zu deiner SqlStatement-Klasse ein abstraktes Interface:

    Code cpp:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    class SQLPP_API ISqlStatement {
    public:
        ISqlStatement( const char *Query ) = 0;
        virtual ~SqlStatement() = 0;
     
        virtual void AddNumber( int Number ) = 0;
        virtual void AddStr( const char *Str ) = 0;
     
        virtual const char *GetQuery() const = 0;
    };

    Davon leitest du dann deine SqlStatement-Klasse ab.

    In der DLL erstellst du zwei neue Funktionen, die dir ein SqlStatement erzeugen/löschen. Dein Hauptprogramm kennt nur das Interface, nicht die davon abgeleitete SqlStatement-Klasse selbst!

    ISqlStatement* CreateStatement( const char* Query );
    ReleaseStatement( ISqlStatement* pStatement );

    Die beiden Funktionen kannst du exportieren und dir damit im Hauptprogramm einen ISqlStatement-Pointer erzeugen lassen. Damit hast du nur Zugriff auf die Interface-Funktionen, das aber hinterrücks ein volles SqlStatement ist. Dadurch liegt der Code von SqlStatement komplett in der DLL und da hast alles eindeutig auf einem Heap.
     

  5. #5
    Avatar von FBIagent
    FBIagent FBIagent ist offline Mitglied Brokat
    Registriert seit
    Jan 2005
    Beiträge
    281
    Das heißt also um etwas objektorientierter zu bleiben noch einmal eine Klasse die
    dann einen Member vom Typ ISqlStatement* hat, nochmal die gleichen Methoden
    Deklariert und Definiert und im C'tor/D'tor die beiden Funktionen aufrufen.

    Hm... da muss ich mir überlegen ob das mitliefern der MSVC++ runtime libraries nicht
    doch die Lösung mit weniger Overhead ist.

    Best wishes
    FBIagent
     
    Don't blame people for bugs. Work together to make things better. No
    finger pointing! Not ever! A good rule is to Never Assume An Attack.
    If you find yourself getting angry, assume it's a misunderstanding, not an
    attack.

Ähnliche Themen

  1. Speicherzugriffsfehler
    Von NoPanic2007 im Forum C/C++
    Antworten: 5
    Letzter Beitrag: 11.09.07, 11:11
  2. Speicherzugriffsfehler
    Von sunmania im Forum C/C++
    Antworten: 1
    Letzter Beitrag: 25.06.07, 10:13
  3. C++ SDL Speicherzugriffsfehler
    Von kle-ben im Forum C/C++
    Antworten: 11
    Letzter Beitrag: 09.12.05, 19:27
  4. Cedega - Speicherzugriffsfehler?
    Von _henrik im Forum Linux & Unix
    Antworten: 0
    Letzter Beitrag: 22.07.05, 13:26
  5. Speicherzugriffsfehler
    Von vaporizer im Forum C/C++
    Antworten: 3
    Letzter Beitrag: 27.05.04, 10:02