2011-01-23 32 views
5

xem xét mã này (VS2008):Chuyển đến trước khi khởi tạo biến gây ra trình biên dịch lỗi

void WordManager::formatWords(std::string const& document) 
{ 
    document_ = document; 
    unsigned int currentLineNo = 1; 

    size_t oldEndOfLine = 0; 
    size_t endOfLine = document_.find('\n'); 
    while(endOfLine != std::string::npos) 
    { 
     std::string line = document_.substr(oldEndOfLine, (endOfLine - oldEndOfLine)); 
     if(line.size() < 2) 
     { 
      oldEndOfLine = endOfLine + 1; 
      endOfLine = document_.find('\n', oldEndOfLine); 
      continue; 
     } 

     std::vector<std::string> words = Utility::split(line); 
     for(unsigned int i(0); i < words.size(); ++i) 
     { 
      if(words[i].size() < 2) 
       continue; 
      Utility::trim(words[i], WordManager::delims); 
      Utility::normalize(words[i], WordManager::replace, WordManager::replaceWith); 

      if(ruleOne(words[i]) && ruleTwo(words[i])) 
      { 
       std::set<Word>::iterator sWIter(words_.find(Word(words[i]))); 

       if(sWIter == words_.end()) 
        words_.insert(Word(words[i])).first->addLineNo(currentLineNo); 
       else 
        sWIter->addLineNo(currentLineNo); 
      } 
     } 
     ++currentLineNo; 

     oldEndOfLine = endOfLine + 1; 
     endOfLine = document_.find('\n', oldEndOfLine); 
    } 
} 

Nếu điều quan trọng là: đây là mã từ một bài tập về nhà sử dụng để lọc ra và sửa đổi từ trong một tài liệu. document giữ tài liệu (đọc trước đó từ tập tin)

tôi muốn giới thiệu một goto độc hại vì tôi nghĩ rằng nó thực sự sạch hơn trong trường hợp này như sau:

void WordManager::formatWords(std::string const& document) 
{ 
    document_ = document; 
    unsigned int currentLineNo = 1; 

    size_t oldEndOfLine = 0; 
    size_t endOfLine = document_.find('\n'); 
    while(endOfLine != std::string::npos) 
    { 
     std::string line = document_.substr(oldEndOfLine, (endOfLine - oldEndOfLine)); 
     // HERE!!!!!! 
     if(line.size() < 2) 
      goto SkipAndRestart; 

     std::vector<std::string> words = Utility::split(line); 
     for(unsigned int i(0); i < words.size(); ++i) 
     { 
      if(words[i].size() < 2) 
       continue; 
      Utility::trim(words[i], WordManager::delims); 
      Utility::normalize(words[i], WordManager::replace, WordManager::replaceWith); 

      if(ruleOne(words[i]) && ruleTwo(words[i])) 
      { 
       std::set<Word>::iterator sWIter(words_.find(Word(words[i]))); 

       if(sWIter == words_.end()) 
        words_.insert(Word(words[i])).first->addLineNo(currentLineNo); 
       else 
        sWIter->addLineNo(currentLineNo); 
      } 
     } 

SkipAndRestart: 
     ++currentLineNo; 

     oldEndOfLine = endOfLine + 1; 
     endOfLine = document_.find('\n', oldEndOfLine); 
    } 
} 

hay không này là một sự lựa chọn thiết kế tốt là không thích hợp vào lúc này. Trình biên dịch than phiền error C2362: initialization of 'words' is skipped by 'goto SkipAndRestart'

Tôi không hiểu lỗi này. Tại sao nó quan trọng, và một lỗi, rằng các từ khởi tạo được bỏ qua? Đó là chính xác những gì tôi muốn xảy ra, tôi không muốn nó làm việc nhiều hơn, chỉ cần khởi động lại vòng lặp đẫm máu. Macro không tiếp tục làm nhiều hay ít chính xác điều tương tự?

+0

Tôi đã nghĩ rằng đây chỉ là cảnh báo chứ không phải lỗi. Điều gì sẽ xảy ra nếu bạn chỉ sử dụng 'break' thay vì goto? –

+5

Hầu hết mọi người có thể không đồng ý rằng phiên bản 'goto' là" sạch hơn "! –

+0

@Oli: Tôi biết, đó là lý do tại sao tôi nói thiết kế thực tế của điều này là không liên quan; Tôi không muốn bắt đầu một cuộc chiến tranh lửa: P @Paul: Biên dịch. – IAE

Trả lời

13

Bạn đang bỏ qua việc xây dựng các mảng words:

if(line.size() < 2) 
     goto SkipAndRestart; 
    std::vector<std::string> words = Utility::split(line); 
    // ... 
SkipAndRestart: 

Bạn thể đã sử dụng words sau nhãn SkipAndRestart:, đó sẽ là một vấn đề. Bạn không sử dụng nó trong trường hợp của bạn, nhưng biến số words sẽ không bị hủy cho đến khi kết thúc của phạm vi được giới thiệu, cho đến khi trình biên dịch liên quan, biến đó vẫn được sử dụng tại điểm của nhãn.

Bạn có thể tránh điều này bằng cách đặt words bên trong phạm vi riêng của mình:

if(line.size() < 2) 
     goto SkipAndRestart; 
    { 
     std::vector<std::string> words = Utility::split(line); 
     // ... 
    } 
SkipAndRestart: 

Lưu ý rằng tuyên bố continue nhảy đến khi kết thúc vòng lặp, tại một điểm mà bạn thực sự không có thể đặt một nhãn . Đây là một điểm sau khi phá hủy bất kỳ biến cục bộ nào bên trong vòng lặp, nhưng trước khi nhảy trở lại lên đỉnh của vòng lặp.

+0

Câu hỏi của tôi được hướng đến nhiều hơn vì tại sao đây là một vấn đề thực tế. – IAE

+1

Tôi chỉ cần thêm một chút về thời gian hủy diệt sẽ giúp giải thích điều đó. –

+0

Cảm ơn rất nhiều! ^^ – IAE

2

Với goto rằng, bạn đang bỏ qua dòng:

std::vector<std::string> words = Utility::split(line); 

này không chỉ bỏ qua việc tái initilisation, nó bỏ qua việc tạo ra. Bởi vì biến đó được định nghĩa bên trong vòng lặp, nó được tạo mới mỗi khi vòng lặp lặp lại. Nếu bạn bỏ qua việc tạo, bạn không thể sử dụng nó.

Nếu bạn muốn nó được tạo ra một lần, bạn nên di chuyển dòng đó bên ngoài vòng lặp.

tôi sẽ kiềm chế không nghiêng đầu tiên của tôi, mà là để cho bạn biết rằng việc sử dụng gotocleaner trong cùng một câu thường là sai - có những tình huống mà goto là tốt hơn nhưng tôi không chắc chắn đây là một trong số họ. Những gì tôi sẽ nói với bạn là, nếu đây là bài tập về nhà, goto là một ý tưởng tồi - bất kỳ nhà giáo dục nào cũng sẽ cau mày khi sử dụng goto.

+0

Bài tập về nhà đã được bật. Tôi chỉ đang thử nghiệm ở đây. Giáo viên của tôi DOES cau mày và buộc tội tôi mucking mã C++ tốt. – IAE

1

Như mọi khi ai đó nghĩ goto sẽ mã dễ đọc hơn, refactoring để sử dụng (inline) chức năng là ít nhất là tốt và không có goto:

// Beware, brain-compiled code ahead! 
inline void WordManager::giveThisAMeaningfulName(const std::string& word, std::string__size_type currentLineNo) 
{ 
    Utility::trim(word, WordManager::delims); 
    Utility::normalize(word, WordManager::replace, WordManager::replaceWith); 
    if(ruleOne(word) && ruleTwo(word)) 
    { 
     std::set<Word>::iterator sWIter(words_.find(Word(word))); 
     if(sWIter == words_.end()) 
      words_.insert(Word(word)).first->addLineNo(currentLineNo); 
     else 
      sWIter->addLineNo(currentLineNo); 
    } 
} 

void WordManager::formatWords(std::string const& document) 
{ 
    document_ = document; 
    std::string__size_type currentLineNo = 1; 

    std::string line; 
    while(std::getline(line)) 
     if(line.size() > 1) 
     { 
      std::vector<std::string> words = Utility::split(line); 
      if(word.size() > 1) 
       for(unsigned int i(0); i < words.size(); ++i) 
        giveThisAMeaningfulName(words[i], currentLineNo++); 
     } 
} 

tôi đã không làm phiền cố gắng để hiểu được những gì mà vòng lặp bên trong vậy, vì vậy tôi để nó cho bạn để cho nó một cái tên có ý nghĩa. Lưu ý rằng, một khi bạn đã cho nó một cái tên, tôi không cần phải hiểu thuật toán của nó để biết nó làm gì, bởi vì cái tên nói lên tất cả.)

Lưu ý rằng tôi đã thay thế dòng viết tay của bạn -extraction by std::getline(), làm cho mã thậm chí còn nhỏ hơn.

+0

Điều này thật thú vị. Cá nhân tôi nghĩ rằng mã của tôi rất dễ đọc và dễ hiểu! Những gì bạn đã viết là một lựa chọn hợp lệ, nhưng không hiểu ngữ nghĩa của lớp, bạn đã làm rối tung một số phần của hàm. Đó là lỗi của tôi mặc dù, chỉ cần đi để cho thấy rằng tôi cần phải viết mã dễ đọc hơn :) – IAE

+1

@SoulBeaver: Tôi đã không đặt nhiều nỗ lực vào thực sự hiểu điều này và tôi biết lý do tại sao tôi thêm rằng từ chối trách nhiệm ở đầu trang. ':)' Dù sao thì, những gì tôi cố gắng vượt qua là: 'goto' là một cách khó hiểu, không có cấu trúc để nhảy, trong khi một số thay thế có cấu trúc dễ hiểu tồn tại: 'return',' break' , 'if' ... Bất cứ khi nào bạn sử dụng' goto', hãy xem xét việc chia nhỏ mã được đề cập để có thể sử dụng chúng. Sử dụng chúng, tôi đã ngưng tụ mã thành điểm mà SO không thêm thanh cuộn dọc nữa. Và ít mã hơn, dễ hiểu hơn. – sbi

+0

Tôi đang lập trình C++ trong gần hai mươi năm và có _never_ đã sử dụng 'goto'. Tôi vẫn chưa tìm thấy một trường hợp là nó sẽ làm cho mã của tôi rõ ràng hơn so với các tùy chọn tái cấu trúc khác. Cụ thể là khả năng C++ không trả tiền cho các chức năng (bằng nội tuyến), cho phép tôi lấy hầu hết các đoạn mã tùy ý và đặt tên cho chúng (tên của hàm tôi đặt vào), tốt hơn nhiều so với bình luận, cộng với sự phụ thuộc rõ ràng kiểm soát lợi ích này (bạn phải truyền dữ liệu cần thiết một cách rõ ràng cho các hàm đó, và nếu nó quá nhiều, nó làm cho bạn nghĩ rằng bạn đang làm sai) là một công cụ rất có giá trị. – sbi

Các vấn đề liên quan