2011-07-25 35 views
37

Có vẻ như đây sẽ là một bản sao, nhưng có thể nó chỉ hiển nhiên như vậy chưa được hỏi ...Kiểm tra xem biến có được khởi tạo

Đây có phải là cách kiểm tra đúng hay không (không phải con trỏ) được khởi tạo trong một lớp C++?

class MyClass 
{ 
    void SomeMethod(); 

    char mCharacter; 
    double mDecimal; 
}; 

void MyClass::SomeMethod() 
{ 
    if (mCharacter) 
    { 
     // do something with mCharacter. 
    } 

    if (! mDecimal) 
    { 
     // define mDecimal. 
    } 
} 
+2

ý của bạn là "được xác định"? bạn có nghĩa là "có một giá trị" là "khởi tạo"? – NirMH

+0

Tất cả các biến luôn có giá trị (chúng được xác định). Thông thường các trình biên dịch khởi tạo các biến cho một số giá trị tiêu chuẩn, nhưng tôi không chắc chắn nếu điều này là một phần của định nghĩa của C++, theo như tôi biết nó không phải là. Đó là một thực hành tốt để khởi tạo tất cả các biến trước khi sử dụng chúng, để chúng không bao giờ có một số giá trị ngẫu nhiên hoặc phụ thuộc vào thực thi.Sau đó, bạn có thể kiểm tra giá trị hiện tại của họ bất kỳ lúc nào trước khi sử dụng chúng. – Giorgio

+1

@Jay, bạn thực sự cần phải reword câu hỏi của bạn để phù hợp với những từ mà C++ sử dụng. Theo quy tắc C++, mCharacter luôn luôn được LUÔN LUÔN định nghĩa trong MyClass trong ví dụ của bạn. 'char mCharacter' xuất hiện ở đó có nghĩa là nó được định nghĩa. Bây giờ bạn cần phải suy nghĩ về những gì bạn thực sự cần bởi "là mCharacter xác định". Bạn có nghĩa là, "giao cho?". C++ không thể kiểm tra điều đó. "Có giá trị khác với giá trị khởi tạo" không? Có thể, nhưng sau đó, ví dụ của bạn không có bất kỳ giá trị khởi tạo nào, do đó bạn cũng không thể kiểm tra điều đó, trừ khi bạn thêm initializer và constructor. Trong hình thức này, câu hỏi phải được ghi lại – Andrei

Trả lời

27

Không có cách nào để kiểm tra nội dung của một biến không được xác định hay không. Điều tốt nhất bạn có thể làm là gán một giá trị tín hiệu/sentinel (ví dụ trong hàm tạo) để chỉ ra rằng việc khởi tạo thêm sẽ cần được thực hiện.

+0

Thật không may, câu trả lời này không chính xác. Có _is_ một thực hiện cho các thành viên lớp mà không sử dụng một giá trị sentinel nếu bạn xem xét C++ - 11, xem câu trả lời của tôi dưới đây. – Twonky

18

Biến chưa được xác định sẽ gây ra lỗi biên dịch.

Điều bạn đang hỏi là kiểm tra xem nó có được khởi tạo hay không. Nhưng khởi tạo chỉ là một giá trị, bạn nên chọn và gán vào hàm tạo.

Ví dụ:

class MyClass 
{ 
    MyClass() : mCharacter('0'), mDecimal(-1.0){}; 
    void SomeMethod(); 

    char mCharacter; 
    double mDecimal; 
}; 

void MyClass::SomeMethod() 
{ 
    if (mCharacter != '0') 
    { 
     // touched after the constructor 
     // do something with mCharacter. 
    } 

    if (mDecimal != -1.0) 
    { 
     // touched after the constructor 
     // define mDecimal. 
    } 
} 

Bạn nên khởi tạo một giá trị mặc định sẽ có nghĩa là một cái gì đó trong bối cảnh của logic của bạn, tất nhiên.

+0

Thêm vào câu trả lời này, bạn có thể đặt trong c-tor một giá trị không hợp lệ và kiểm tra xem nó có còn không hợp lệ trong phương thức của bạn hay không. hoặc sử dụng boolean để gắn cờ giá trị đã được thay đổi từ giá trị không hợp lệ ban đầu – NirMH

+0

@NirMH Tôi sẽ nói hầu như luôn luôn nếu bạn không có thông tin để tạo đối tượng hợp lệ khi xây dựng, thì hàm tạo nên được loại bỏ để hỗ trợ một hàm tạo đủ thông tin để tạo một đối tượng hợp lệ. –

1

MyClass là loại lớp POD, các thành viên dữ liệu không tĩnh sẽ không xác định giá trị ban đầu khi bạn tạo phiên bản không tĩnh MyClass, do đó không, đó không phải là cách hợp lệ để kiểm tra xem chúng đã được khởi tạo hay chưa với một giá trị khác không cụ thể ... về cơ bản bạn giả định rằng chúng sẽ không được khởi tạo, không phải là trường hợp vì bạn không khởi tạo giá trị chúng trong một hàm tạo.

Nếu bạn muốn không khởi tạo các thành viên dữ liệu không tĩnh của lớp, tốt nhất bạn nên tạo danh sách khởi tạo và hàm tạo lớp. Ví dụ:

class MyClass 
{ 
    void SomeMethod(); 

    char mCharacter; 
    double mDecimal; 

    public: 
     MyClass(); 
}; 

MyClass::MyClass(): mCharacter(0), mDecimal(0) {} 

Danh sách khởi tạo trong giá trị trên sẽ khởi tạo thành viên dữ liệu của bạn về 0. Bây giờ bạn có thể giả định đúng rằng mọi giá trị khác 0 cho số mCharactermDecimal phải được bạn đặt ở một nơi khác trong mã của bạn và chứa các giá trị khác không bạn có thể hành động đúng cách.

+0

Biến thành viên không được khởi tạo thành giá trị mặc định hoặc ngẫu nhiên khi đối tượng được khởi tạo ** trừ khi ** nó được thực hiện rõ ràng trong c-tor. Nhiều runtimes gỡ lỗi sẽ khởi tạo bộ nhớ đến một giá trị cụ thể khi nó được cấp phát (mới), nếu không nó là UB và giá trị sẽ là bất cứ điều gì bộ nhớ chứa trước khi instantiation. –

+0

Phương thức khởi tạo mặc định cho 'MyClass', vì nó không khởi tạo giá trị bất kỳ biến nào trong phiên bản gốc của OP, sẽ tạo giá trị ngẫu nhiên cho các thành viên dữ liệu ... giá trị ngẫu nhiên (nghĩa là giá trị đã tồn tại trong bộ nhớ) là khởi tạo mặc định cho loại dữ liệu POD. – Jason

+0

"mặc định được khởi tạo với giá trị ngẫu nhiên" - Câu lệnh này ngụ ý rằng theo mặc định, một số loại khởi tạo xảy ra, lưu trữ một giá trị ngẫu nhiên trong biến. Thuật ngữ chính xác chỉ đơn giản là "uninitialized" –

1

Nếu bạn muốn kiểm tra xem các biến thành viên đã được khởi tạo hay chưa, bạn có thể thực hiện điều này bằng cách gán cho chúng các giá trị sentinel trong hàm tạo. Chọn giá trị sentinel làm giá trị sẽ không bao giờ xảy ra khi sử dụng bình thường biến đó. Nếu một biến toàn bộ phạm vi được coi là hợp lệ, bạn có thể tạo một boolean để cho biết liệu nó đã được khởi tạo hay chưa.

#include <limits> 

class MyClass 
{ 
    void SomeMethod(); 

    char mCharacter; 
    bool isCharacterInitialized; 
    double mDecimal; 

    MyClass() 
    : isCharacterInitialized(false) 
    , mDecimal(std::numeric_limits<double>::quiet_NaN()) 
    {} 


}; 


void MyClass::SomeMethod() 
{ 
    if (isCharacterInitialized == false) 
    { 
     // do something with mCharacter. 
    } 

    if (mDecimal != mDecimal) // if true, mDecimal == NaN 
    { 
     // define mDecimal. 
    } 
} 
9

Tùy thuộc vào ứng dụng của bạn (và đặc biệt nếu bạn đang sử dụng tăng), bạn có thể muốn xem xét boost::optional.

http://www.boost.org/doc/libs/1_47_0/libs/optional/doc/html/index.html

Nó có tài sản bạn đang tìm kiếm, theo dõi cho dù khe thực sự giữ một giá trị hay không. Theo mặc định nó được xây dựng để không giữ một giá trị và đánh giá sai, nhưng nếu nó đánh giá đúng, bạn được phép dereference nó và nhận được giá trị được bao bọc.

class MyClass 
{ 
    void SomeMethod(); 

    optional<char> mCharacter; 
    optional<double> mDecimal; 
}; 

void MyClass::SomeMethod() 
{ 
    if (mCharacter) 
    { 
     // do something with *mCharacter. 
     // (note you must use the dereference operator) 
    } 

    if (! mDecimal) 
    { 
     // call mDecimal.reset(expression) 
     // (this is how you assign an optional) 
    } 
} 

Ví dụ khác có trong tài liệu.

http://www.boost.org/doc/libs/1_47_0/libs/optional/doc/html/boost_optional/examples.html

2

Theo mặc định, không bạn không thể biết nếu một biến (hoặc con trỏ) có hoặc chưa được khởi tạo. Tuy nhiên, vì mọi người khác đang nói cho bạn cách tiếp cận "dễ dàng" hoặc "bình thường", tôi sẽ cung cấp cho bạn một thứ khác để suy nghĩ. Đây là cách bạn có thể theo dõi những thứ như thế (không, cá nhân tôi sẽ không bao giờ làm điều này, nhưng có lẽ bạn có nhu cầu khác với tôi).

class MyVeryCoolInteger 
{ 
public: 
    MyVeryCoolInteger() : m_initialized(false) {} 

    MyVeryCoolInteger& operator=(const int integer) 
    { 
     m_initialized = true; 
     m_int = integer; 
     return *this; 
    } 

    int value() 
    { 
     return m_int; 
    } 

    bool isInitialized() 
    { 
     return m_initialized; 
    } 

private: 
    int m_int; 
    bool m_initialized; 
}; 
+0

Số nguyên rất thú vị của bạn tồn tại, trừu tượng hơn như một khuôn mẫu, và được gọi là 'boost :: optional'. (Xem câu trả lời của tôi.) Không có gì sai khi sử dụng nó nếu đó là loại tình huống bạn có. Mặc dù chắc chắn nếu bạn có hàng triệu số nguyên, nó cũng có thể trả tiền để chọn một giá trị ma thuật để chỉ ra "không có giá trị". – HostileFork

+0

Ồ không nghi ngờ gì. Nhưng như đã đề cập, tôi nghĩ tôi sẽ đưa ra một câu trả lời thay thế. –

0

Không có cách nào trong ngôn ngữ C++ để kiểm tra xem biến có được khởi tạo hay không (mặc dù loại lớp có trình xây dựng sẽ được khởi chạy tự động).

Thay vào đó, những gì bạn cần làm là cung cấp (các) hàm khởi tạo để khởi tạo lớp của bạn thành trạng thái hợp lệ. Các trình kiểm tra mã tĩnh (và có thể một số trình biên dịch) có thể giúp bạn tìm các biến bị thiếu trong các hàm tạo. Bằng cách này, bạn không để lo lắng về trạng thái không có thật và séc if trong phương thức của bạn có thể biến mất hoàn toàn.

1

Không có cách nào hợp lý để kiểm tra xem liệu giá trị đã được khởi tạo chưa.

Nếu bạn quan tâm về việc có điều gì đó đã được khởi chạy hay không, thay vì cố gắng kiểm tra, hãy đặt mã vào (các) hàm khởi tạo để đảm bảo chúng luôn được khởi tạo và thực hiện với nó.

0

Nếu ví dụ bạn sử dụng chuỗi thay vì ký tự, bạn có thể làm điều gì đó như thế này:

//a is a string of length 1 
    string a; 
    //b is the char in which we'll put the char stored in a 
    char b; 
    bool isInitialized(){ 
     if(a.length() != NULL){ 
     b = a[0]; 
     return true; 
     }else return false; 
    } 
1

Với C++ - 11 bạn có thể xem xét lưu trữ các biến sử dụng con trỏ thông minh. Cân nhắc MVE này, nơi toString() hành vi phụ thuộc vào bar được khởi tạo hay không:

#include <memory> 
#include <sstream> 

class Foo { 

private: 
    std::shared_ptr<int> bar; 

public: 
    Foo() {} 
    void setBar(int bar) { 
     this->bar = std::make_shared<int>(bar); 
    } 
    std::string toString() const { 
     std::ostringstream ss; 
     if (bar)   // bar was set 
      ss << *bar; 
     else    // bar was never set 
      ss << "unset"; 
     return ss.str(); 
    } 
}; 

Sử dụng mã này

Foo f; 
std::cout << f.toString() << std::endl; 
f.setBar(42); 
std::cout << f.toString() << std::endl; 

sản xuất sản lượng

unset 
42 
1

Với C++ 17 bạn có thể sử dụng std::optional để kiểm tra xem biến có được khởi tạo hay không:

#include <optional> 
#include <iostream> // needed only for std::cout 

int main() { 
    std::optional<int> variable; 

    if (!variable) { 
     std::cout << "variable is NOT initialized\n"; 
    } 

    variable = 3; 

    if (variable) { 
     std::cout << "variable IS initialized and is set to " << *variable << '\n'; 
    } 

    return 0; 
} 

này sẽ cho kết quả:

variable is NOT initialized 
variable IS initialized and is set to 3 

Để sử dụng std::optional trong mã snipped mà bạn cung cấp, bạn sẽ phải bao gồm tiêu đề thư viện chuẩn <optional> và thêm std::optional<...> đến khai báo biến tương ứng :

#include <optional> 

class MyClass 
{ 
    void SomeMethod(); 

    std::optional<char> mCharacter; 
    std::optional<double> mDecimal; 
}; 

void MyClass::SomeMethod() 
{ 
    if (mCharacter) 
    { 
     std::cout << *mCharacter; // do something with mCharacter. 
    } 

    if (! mDecimal) 
    { 
     mDecimal = 3.14159; // define mDecimal. 
    } 
} 
Các vấn đề liên quan