【问题标题】:Pointer Issues 2 [Valid C++ Syntax]指针问题 2 [有效的 C++ 语法]
【发布时间】:2009-04-18 03:40:59
【问题描述】:

此版本正在运行。我在整个代码中都使用了// cmets,以更好地说明我遇到的问题。该程序依赖于读取文本文件。采用包含标点符号的段落格式。

可以将上面的内容复制到文本文件中并运行程序。

// 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;
}

【问题讨论】:

  • 我已经尝试自己处理异常,但我还没有达到那个水平。
  • 在代码示例中不要使用
    ,您只需选择代码部分并单击编辑器中的代码按钮(带有 1 和 0 的按钮)

标签: c++ pointers


【解决方案1】:

编辑:我已将此编辑放在顶部,因为它直接回答了您关于 Word 为何损坏的问题。您的复制构造函数错误:

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

这不会复制theObject-&gt;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 字对象。

原始答案:

为什么不直接用std::string 替换Word 类?这将使代码更小,更易于使用。

如果它让您更轻松,请这样做:

typedef std::string Word;

那么你可以这样做:

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

此外,它还有一个额外的好处,就是您无需使用 .c_str() 成员来获取 c 样式的字符串。

编辑:

我还将您的 g_wordArray 更改为 std::vector&lt;Word&gt;。这样你就可以简单地做到这一点:

g_wordArray.push_back(Word(str));

没有更多的动态分配!这一切都为你完成。字数组的大小将仅受您拥有的 RAM 数量的限制,因为当您使用 push_back() 时,std::vector 会根据需要增长。

此外,如果您这样做...猜猜字数统计,您只需这样做:

g_wordArray.size();

无需手动跟踪它们的数量!

编辑:

另外,这段代码被破坏了:

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

因为直到 您尝试读取之后才设置 eof,所以最好使用这种模式:

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

它将在 EOF 上正确停止。

归根结底,如果你做得对,你需要编写的实际代码应该很少。

编辑:

这是我认为您想要的直接实施示例。从菜单项看来,用户的意图似乎是首先选择选项“a”,然后选择“b”零次或多次以过滤掉一些单词,然后最后 c 打印结果(每行一个单词)。此外,实际上并不需要选项“D”,因为点击Ctrl+D 会向程序发送 EOF 并使“while(std::cin &gt;&gt; option)”测试失败。从而结束程序。 (至少在我的操作系统中,Windows 可能是 Ctrl+Z`)。

它也没有努力(你也没有)处理标点符号,但这里是:

#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;
}

【讨论】:

  • +1 很好的答案,尽管“复制构造函数”不是复制构造函数。所以编译器生成的仍然存在,并且执行相同的成员复制,因此同样的问题仍然存在。
  • 确实,不幸的是,他提供的代码存在几个问题。我会在你的评论中编辑。
【解决方案2】:

没有费力地浏览整个 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的析构函数中被删除了。

他们称其为“复制构造函数”,因为这是你应该做的:复制这件事。

【讨论】:

  • This: "Word::Word( const Word* theObject )" 不是复制构造函数。复制构造函数只允许将原始类型的引用、const 引用、volatile 引用或 const volatile 引用作为其第一个也是唯一的必需参数。带有指向 const 的指针的构造函数“只是另一个构造函数”,不会阻止编译器生成具有默认成员复制实现的真正的隐式复制构造函数。
  • @Charles Bailey:哦,是的。你是对的术语。然而,最初关于摧毁另一个实例内部成员的批评仍然存在。
【解决方案3】:

g_wordArray[wrdCount] 是一个有效的 Word 对象吗?我的 C++ 生锈了,但在我看来,这超出了数组的末尾。此外,您似乎在 inFile2 循环中反复践踏 g_wordArray 的内容:对于您读入 str2 的每个字符串,您将重新初始化 g_wordArray 中的第一个 str2.length() 条目。这可以解释成员变量的明显破坏。最后,我不确定这一点,但是在您将新值读入 str2 后,str2.c_str() 返回的指针是否仍然有效?因此,您的 GetWord() 循环可能会从 strlen 获取垃圾值,然后滚落到永远不会着陆。

【讨论】:

    猜你喜欢
    • 2021-07-14
    • 1970-01-01
    • 2014-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-15
    • 2011-08-02
    • 2011-04-15
    相关资源
    最近更新 更多