Проблемы с указателями 2 [Действительный синтаксис C ++] - PullRequest
0 голосов
/ 18 апреля 2009

Эта версия работает. Я сделал // комментариев по всему коду, чтобы лучше показать, с чем у меня проблемы. Программа зависит от чтения текстового файла. В формате абзацы с пунктуацией включены.

Можно скопировать это и выше в текстовый файл и запустить программу.

// Word.cpp

#define _CRT_SECURE_NO_WARNINGS // disable warnings for strcpy
#define ARRY_SZ 100
#include <iostream>
#include <fstream>
#include "Word.h"

using namespace std;

Word::Word( const char* word )
{
    ptr_ = new char[ strlen( word ) + 1 ];
    strcpy( ptr_, word  );  
    len_ = strlen( ptr_ );
}

Word::Word( const Word* theObject ) 
{
    ptr_ = theObject->ptr_;
    len_ = theObject->len_;
}

Word::~Word()
{
    delete [] ptr_;
    ptr_ = NULL;
}

char Word::GetFirstLetterLower()
{
    // I want to use ptr_ and len_ here
    // but the information is gone!
    char c = 0;
    return c;
}

char* Word::GetWord()
{
    for (int x = 0; x < strlen( (char*)ptr_ ); x++)
        ptr_[x];  // Results in a crash.

    return ptr_;
}

// Word.h

const int FILE_PATH_SZ = 512;
class Word
{
private:
    char* ptr_;
    int len_;
public:
    Word( const Word* ); // an appropriate default constructor
    Word( const char* );
    ~Word( );
    char GetFirstLetterLower( );
    char* GetWord( );
    static char fileEntry[ FILE_PATH_SZ ];
};

// main.cpp

#ifdef  _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <iostream>
#include <fstream>
#include <string>
#endif
#include "Word.h"
using namespace std;

const int WORD_SZ = 100;
Word** g_wordArray;
int g_arrSz;

static char filePath[ FILE_PATH_SZ ] = {};
void FreeWordArray();

int main( const int argc, const char **argv )
{
    int     wrdCount = 0;
    char    usrMenuOption     = 0,
            getFirstLetter          = 0,
            tmpArray[WORD_SZ] = {},
            *getWord = 0;
    string  str, 
            str2;
    ifstream  inFile, 
              inFile2;

    do 
    {
        cout << "Please make a selection: \n\
a) Read a text file\n\
b) Remove words starting with letter\n\
c) Print words to console\n\
d) Quit\n";
        cin  >> usrMenuOption;
        switch( usrMenuOption )
        {
            case'A':
            case'a':
                cout << "Enter a file name: ";
                cin.sync();
                cin  >> filePath;
                inFile.open( filePath );
                if ( !inFile ) return -1;
                inFile >> str; // prime the eof flag
                while ( !inFile.eof() )
                {        
                    inFile >> str;
                    wrdCount++;
                    g_wordArray = new Word *[wrdCount];
                }
                inFile.close();
                inFile2.open( filePath );

                while( !inFile2.eof() )
                {
                    inFile2 >> str2;
                    for ( unsigned x = 0; x < str2.length(); x++ )
                    g_wordArray[x] = new Word( str2.c_str() );
                }
                cout << wrdCount << " Words read from the file " << endl;
                inFile2.close();
                break;
            case'B':
            case'b':
                getFirstLetter = g_wordArray[wrdCount]->GetFirstLetterLower();
                //getWord = g_wordArray[wrdCount]->GetWord();
                cout << getWord << endl;
                break;
            case'C':
            case'c':
                break;
            case'D':
            case'd':
                cout << "Quit Requested. " << endl;
                break;
            default:
                cout << '"' << usrMenuOption << '"' << " Not Defined! " << endl;
        }
    } while (usrMenuOption != 'D' && usrMenuOption != 'd');

#ifdef _DEBUG
    _CrtDumpMemoryLeaks();
#endif
    cin.ignore();
    return 0;
}

void FreeWordArray()
{
    // free the memory that is allocated
    return;
}

Ответы [ 3 ]

9 голосов
/ 18 апреля 2009

РЕДАКТИРОВАТЬ: Я поместил это изменение вверху, так как оно прямо отвечает на ваш вопрос о том, почему Word не работает. Ваш конструктор копирования неверен:

Word::Word( const Word* theObject ) 
{
    ptr_ = theObject->ptr_;
    len_ = theObject->len_;
}

это не делает копию того, на что указывает theObject->ptr_, только указатель. Таким образом, у вас есть два объекта Word, указывающих на одну и ту же внутреннюю строку. Это очень плохо, когда объект Word удаляется. Правильная реализация (с использованием техник, которые вы применили, я рекомендую против них) была бы такой:

Word::Word( const Word* theObject ) 
{
    ptr_ = new char[theObject->len_ + 1 ];
    strcpy( ptr_, theObject->ptr_  );  
    len_ = theObject->len_;
}

РЕДАКТИРОВАТЬ: Earwicker также отметил следующее:

... хотя этот "конструктор копирования" не является конструктором копирования. Итак генерируемый компилятором существует, и делает то же самое для каждого члена копировать, и, следовательно, та же проблема все еще существует.

Чтобы исправить это, вам нужно создать правильный конструктор копирования, который должен иметь прототип:

Word::Word(const Word &theObject);

Также этот код здесь:

while ( !inFile.eof() )
{        
    inFile >> str;
    wrdCount++;
    g_wordArray = new Word *[wrdCount];
}

течет как сито! Вы перераспределяете g_wordArray после каждого прочитанного слова и полностью забываете удалить предыдущее. Я еще раз покажу разумную реализацию, используя методы, которые вы пытаетесь использовать.

while (inFile >> str)
{        
    inFile >> str;
    wrdCount++;
}
g_wordArray = new Word *[wrdCount];

Обратите внимание на то, как он считает слова, затем выделяет место один раз , после того, как знает, сколько выделить. Теперь g_wordArray готов к использованию для wrdCount объектов слова.

ОРИГИНАЛЬНЫЙ ОТВЕТ:

почему бы вам просто не заменить класс Word на std::string? Это сделало бы код намного меньше и с ним легче работать.

Если вам будет проще, просто сделайте это:

typedef std::string Word;

тогда вы можете сделать так:

Word word("hello");
char first_char = word[0];

плюс к этому добавлен бонус, исключающий необходимость использования члена .c_str() для получения строки в стиле c.

EDIT:

Я бы тоже изменил ваш g_wordArray на std::vector<Word>. Таким образом, вы можете просто сделать это:

g_wordArray.push_back(Word(str));

больше нет динамического распределения! это все сделано для тебя. Размер массива слов будет ограничен только объемом вашей оперативной памяти, поскольку std::vector увеличивается по мере необходимости при использовании push_back().

Кроме того, если вы сделаете это ... угадайте, что для подсчета слов вы просто сделаете это:

g_wordArray.size();

нет необходимости вручную отслеживать их количество!

EDIT:

Также этот код не работает:

while( !inFile2.eof() )
{
    inFile2 >> str2;
    ...
}

, поскольку eof не установлен до после попытки чтения, вам лучше использовать этот шаблон:

while(inFile2 >> str2)
{
    ...
}

, который правильно остановится на EOF.

Суть в том, что если вы все сделаете правильно, фактического кода, который вам нужно написать, должно быть очень мало.

EDIT:

Вот пример прямой реализации того, что, я думаю, вы хотите. Из пунктов меню кажется, что пользователь должен сначала выбрать опцию «a», затем «b» ноль или более раз, чтобы отфильтровать некоторые слова, а затем, наконец, c, чтобы напечатать результаты (по одному слову в строке). Кроме того, опция 'D' на самом деле не нужна, поскольку нажатие Ctrl+D отправляет EOF программе и приводит к сбою теста "while(std::cin >> option)". Таким образом, окончание программы. (По крайней мере, в моей ОС это может быть Ctrl + Z` для Windows).

Также он не прикладывает усилий (как и ваши) к пунктуации, но вот он:

#include <string>
#include <vector>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <functional>
#include <iterator>

struct starts_with : public std::binary_function<std::string, char, bool> {
    bool operator()(const std::string &s, char ch) const {
        return s[0] == ch;
    }
};

void print_prompt() {
    std::cout << "Please make a selection: \na) Read a text file\nb) Remove words starting with letter\nc) Print words to console" << std::endl;
}

int main( const int argc, const char **argv) {
    std::vector<std::string> file_words;
    char option;
    print_prompt();
    while(std::cin >> option) {
        switch(option) {
        case 'a':
        case 'A':
            std::cout << "Enter a file name: ";
            // scope so we can have locals declared
            {
                std::string filename;
                std::string word;
                std::cin >> filename;
                int word_count = 0;
                std::ifstream file(filename.c_str());
                while(file >> word) {
                    file_words.push_back(word);
                }
                std::cout << file_words.size() << " Words read from the file " << std::endl;
            }
            break;
        case 'b':
        case 'B':
            // scope so we can have locals declared
            {
                std::cout << "Enter letter to filter: ";
                char letter;
                std::cin >> letter;

                // remove all words starting with a certain char
                file_words.erase(std::remove_if(file_words.begin(), file_words.end(), std::bind2nd(starts_with(), letter)), file_words.end());
            }
            break;          

        case 'c':
        case 'C':
            // output each word to std::cout separated by newlines
            std::copy(file_words.begin(), file_words.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
            break;
        }
        print_prompt();
    }
    return 0;
}
2 голосов
/ 18 апреля 2009

Не пробираясь сквозь весь ваш main.cpp, я заметил проблему в вашем наборе конструктора / деструктора:

Word::Word( const Word* theObject ) 
{
    ptr_ = theObject->ptr_;
    len_ = theObject->len_;
}

Word::~Word()
{
    delete [] ptr_;
    ptr_ = NULL;
}

Обратите внимание, что в конструкторе копирования вы просто назначаете свои внутренние указатели в соответствии с объектом, из которого копируете. Но в вашем деструкторе вы удаляете данные по своим внутренним указателям.

Вот где это может заразиться:

Word w1 = Word("hello");
Word w2 = Word(w1);
delete w2;

Теперь, что содержится в вашем первом слове? Указатель будет существовать, но данные, на которые он ссылается, были удалены в деструкторе w2.

Они называют это «конструктором копирования», потому что это то, что вы должны делать: копировать вещь.

0 голосов
/ 18 апреля 2009

Является ли g_wordArray[wrdCount] допустимым объектом Word? Мой C ++ ржавый, но мне кажется, что это за пределами массива. Кроме того, вы, похоже, неоднократно попираете содержимое g_wordArray в цикле inFile2: для каждой строки, которую вы читаете в str2, вы собираетесь повторно инициализировать первые записи str2.length () в g_wordArray. Это объяснило бы кажущееся засорение переменных-членов. Наконец, я не уверен в этом, но действительно ли указатель, возвращаемый str2.c_str (), остается действительным после того, как вы прочитали новое значение в str2? Таким образом, ваш цикл GetWord () может получать значение мусора из strlen и уходить в никогда и никогда не приземлится.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...