Có một số phương pháp.
1) Ném ngoại lệ. Điều này hữu ích nếu bạn muốn GetAccountById
trả lại tài khoản theo giá trị và việc sử dụng ngoại lệ phù hợp với mô hình lập trình của bạn. Một số sẽ cho bạn biết rằng ngoại lệ là "có nghĩa" chỉ được sử dụng trong các trường hợp ngoại lệ. Những thứ như "hết bộ nhớ" hoặc "máy tính đang cháy". Điều này là rất có thể gây tranh cãi, và đối với mọi lập trình viên bạn thấy ai nói ngoại lệ không phải là để kiểm soát dòng chảy, bạn sẽ tìm thấy một người khác (bao gồm cả tôi), người nói rằng ngoại lệ có thể được sử dụng để kiểm soát luồng. Bạn cần phải suy nghĩ về điều này và quyết định cho chính mình.
Account GetAccountById(unsigned int id) const
{
if(account_not_found)
throw std::runtime_error("account not found");
}
2) Không trả lại và Account
theo giá trị. Thay vào đó, quay trở lại bởi con trỏ (pointer tốt thông minh), và trở về NULL khi bạn không tìm thấy tài khoản:
boost::shared_ptr<Account> GetAccountById(unsigned int id) const
{
if(account_not_found)
return NULL;
}
3) Quay trở lại một đối tượng mà có một 'hiện diện' cờ chỉ ra hay không mục dữ liệu là hiện tại. Boost.Optional là một ví dụ về thiết bị như vậy, nhưng trong trường hợp bạn không thể sử dụng Boost ở đây là đối tượng có khuôn mẫu có thành viên bool
là true
khi mục dữ liệu có mặt và là false
khi không. Bản thân mục dữ liệu được lưu trữ trong thành viên value_
. Nó phải được cấu hình mặc định.
template<class Value>
struct PresenceValue
{
PresenceValue() : present_(false) {};
PresenceValue(const Value& val) : present_(true), value_(val) {};
PresenceValue(const PresenceValue<Value>& that) : present_(that.present_), value_(that.value_) {};
explicit PresenceValue(Value val) : present_(true), value_(val) {};
template<class Conv> explicit PresenceValue(const Conv& conv) : present_(true), value_(static_cast<Value>(conv)) {};
PresenceValue<Value>& operator=(const PresenceValue<Value>& that) { present_ = that.present_; value_ = that.value_; return * this; }
template<class Compare> bool operator==(Compare rhs) const
{
if(!present_)
return false;
return rhs == value_;
}
template<class Compare> bool operator==(const Compare* rhs) const
{
if(!present_)
return false;
return rhs == value_;
}
template<class Compare> bool operator!=(Compare rhs) const { return !operator==(rhs); }
template<class Compare> bool operator!=(const Compare* rhs) const { return !operator==(rhs); }
bool operator==(const Value& rhs) const { return present_ && value_ == rhs; }
operator bool() const { return present_ && static_cast<bool>(value_); }
operator Value() const;
void Reset() { value_ = Value(); present_ = false; }
bool present_;
Value value_;
};
Để đơn giản, tôi sẽ tạo ra một typedef cho Account
:
typedef PresenceValue<Account> p_account;
... và sau đó quay trở lại này từ chức năng của bạn:
p_account GetAccountByIf(...)
{
if(account_found)
return p_account(the_account); // this will set 'present_' to true and 'value_' to the account
else
return p_account(); // this will set 'present_' to false
}
Sử dụng này rất đơn giản:
p_account acct = FindAccountById(some_id);
if(acct.present_)
{
// magic happens when you found the account
}
+1 đánh bại tôi đến một vài giây. :) – casablanca
Tôi rất thích câu trả lời của Juraj bên dưới, bởi vì nó mã hóa tính vô dụng trong kiểu, làm tăng sự an toàn kiểu của mã. Đặc biệt, sử dụng phương pháp của bạn có thể vô tình quên để kiểm tra sự trở lại, trong khi nếu trả lại là giá trị, sau đó bạn phải làm cho nó sử dụng nó :) –
Tôi nghĩ vấn đề chính của tôi ở đây là đặt tên của hàm. GetX() với tôi luôn luôn nên trả về X (nếu nó không có đó là một ngoại lệ). Nếu có một khả năng của X không tồn tại thì sẽ có hàm findX() trả về một trình lặp (hoặc trình lặp như đối tượng). Nếu không thể tìm thấy đối tượng, điều này có thể được xác định từ trình lặp nếu nó được tìm thấy thì trình vòng lặp có thể được chuyển đổi thành tham chiếu của đối tượng.Việc truyền tham số đầu ra không trực quan và yêu cầu bạn có thể tạo một đối tượng không hợp lệ/trống (được chuyển vào và điền). Các đối tượng không hợp lệ/trống là lỗi dễ xảy ra. –