可以/不能返回结构的C ++方法

时间:2010-12-13 15:38:24

标签: c++ struct

我有一个C ++结构和一个方法:

struct Account
{
    unsigned int id;
    string username;
    ...
};


Account GetAccountById(unsigned int id) const { }

如果帐户存在,我可以返回帐户结构,但如果没有帐户该怎么办?

我想过:

  • 结构上的“有效”标志(因此可以返回空标志,并将其设置为false)
  • 如果输出有效,则设置另一个“有效”指针(const string& id,int * is_ok)
  • 改为返回帐户*,并返回指向结构的指针,如果不存在则返回NULL?

有最好的方法吗?

11 个答案:

答案 0 :(得分:13)

你忘记了最明显的一个,用C ++:

bool GetAccountById(unsigned int id, Account& account);

如果帐户存在,请返回true并填写提供的参考,否则返回false

使用指针可以为null并且具有:

的事实也可能是方便的
bool GetAccountById(unsigned int id, Account* account);

如果帐户ID存在,则可以将其定义为返回true,但如果指针非空,则仅(当然)填写提供的帐户。有时候能够测试存在是否方便,这样就可以节省必须专门用于此目的的方法。

这是你喜欢的品味问题。

答案 1 :(得分:7)

根据给出的选项,我将返回Account*。但返回指针可能会对界面产生一些不良影响。

另一种可能性是在没有此类帐户时throw例外。您也可以尝试boost::optional

答案 2 :(得分:6)

您也可以尝试null object pattern

答案 3 :(得分:3)

这取决于您认为不存在的帐户的可能性。

如果它确实非常特殊 - 深入银行系统内部的数据应该是有效的 - 那么可能会抛出异常。

如果它在用户界面级别,验证数据,则可能不会抛出异常。

返回指针意味着有人必须释放已分配的内存 - 这更加混乱。

您可以使用“标记ID”(例如0)来表示“帐户无效”吗?

答案 4 :(得分:2)

我会使用Account*并向方法添加文档注释,声明返回值可以为NULL。

答案 5 :(得分:2)

有几种方法。

1)抛出异常。如果您希望GetAccountById按值返回帐户,并且异常的使用符合您的编程模型,则此功能非常有用。有些人会告诉你,只有在特殊情况下才会使用例外情况。像“内存不足”或“计算机着火”之类的东西。这是非常值得商榷的,对于每个程序员而言,如果您发现异常不是流量控制的话,您会发现另一个(包括我自己)说异常可以用于流量控制。你需要考虑这个并自己决定。

Account GetAccountById(unsigned int id) const
{
  if( account_not_found )
    throw std::runtime_error("account not found");
}

2)不要按值返回Account。相反,返回指针(最好是智能指针),并在找不到帐户时返回NULL:

boost::shared_ptr<Account> GetAccountById(unsigned int id) const
{
  if( account_not_found )
    return NULL;
}

3)返回一个具有'presence'标志的对象,该标志指示数据项是否存在。 Boost.Optional是此类设备的示例,但是如果您不能在此处使用Boost,则会在数据项存在时模板化对象,其bool成员为true,如果不是,则为false。数据项本身存储在value_成员中。它必须是默认的可构造的。

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

为简单起见,我会为Account创建一个typedef:

typedef PresenceValue<Account> p_account;

...然后从你的函数中返回:

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
}

使用它很简单:

p_account acct = FindAccountById(some_id);
if( acct.present_ )
{
  // magic happens when you found the account
}

答案 6 :(得分:0)

除了返回引用之外的另一种方法是返回指针。如果帐户存在,则返回其指针。否则,返回NULL。

答案 7 :(得分:0)

还有另一种类似于“有效”模式的方式。我正在开发一个现在有很多这样的东西的应用程序。但我的ID永远不会小于1(它们都是PostgreSQL数据库中的SERIAL字段)所以我只为每个结构(或我的情况下的类)都有一个默认构造函数,它用-1和{{初始化id 1}}如果isValid()不等于-1,则返回true的方法。适合我。

答案 8 :(得分:0)

我愿意:

class Bank
{
  public:

    class Account {};
    class AccountRef
    {
        public:
            AccountRef():                   m_account(NULL)  {}
            AccountRef(Account const& acc)  m_account(&acc)  {}
            bool isValid() const                             { return m_account != NULL);}
            Account const& operator*()                       { return *m_account; }
            operator bool()                                  { return isValid(); }
        private:
            Account const* m_account;
    }; 
    Account const& GetAccountById(unsigned int id) const
    {
        if (id < m_accounts.size())
        {    return m_accounts[id];
        }
        throw std::outofrangeexception("Invalid account ID");
    }

    AccountRef FindAccountById(unsigned int id) const
    {
        if (id < m_accounts.size())
        {    return AccountRef(m_accounts[id]);
        }
        return AccountRef();
    }
  private:
    std::vector<Account>   m_accounts;
};

一个名为get的方法应该总是返回(恕我直言)所要求的对象。如果它不存在那么这是一个例外。如果某些内容可能不存在,那么您还应该提供一个find方法,以确定该对象是否存在,以便用户可以对其进行测试。

int main()
{
    Bank    Chase;

    // Get a reference
    // As the bank ultimately ownes the account.
    // You just want to manipulate it.
    Account const&   account = Chase.getAccountById(1234);

    // If there is the possibility the account does not exist then use find()
    AccountRef ref = Chase.FindAccountById(12345);
    if ( !ref )
    {     // Report error
          return 1;
    }
    Account const&  anotherAccount = *ref;
}

现在我可以使用指针而不是创建AccountRef。问题在于指针没有所有权语义,因此没有真正指示谁应该拥有(并因此删除)指针。

因此,我喜欢将指针包装在某个容器中,允许用户仅按照我的需要操作对象。在这种情况下,AccountRef不会公开指针,因此AccountRef用户无法实际尝试删除该帐户。

在这里,您可以检查AccountRef是否有效并提取对帐户的引用(假设它是有效的)。因为对象只包含一个指针,所以编译器可能会对此进行优化,以至于这并不比传递指针更昂贵。好处是用户不会不小心滥用我给他们的东西。

总结:AccountRef没有实际的运行时成本。然而提供了类型安全性(因为它隐藏了指针的使用)。

答案 9 :(得分:0)

我喜欢将您建议的内容与Valid标记以及其他人建议的null对象模式结合使用。

我有一个名为Status的基类,我继承自我想要用作返回值的对象。我会把大部分内容都放在讨论之外,因为它涉及的内容有点多,但它看起来像这样

class Status
{
   public:
     Status(bool isOK=true) : mIsOK(isOK)
     operator bool() {return mIsOK;}
   private
     bool mIsOK
};

现在你已经

class Account : public Status
{
   public:
     Account() : Status(false)
     Account(/*other parameters to initialize an account*/) : ...
     ...
};

现在,如果您创建一个没有参数的帐户:

Account A;

它无效。但是,如果您创建一个包含数据的帐户

Account A(id, name, ...);

这是有效的。

您可以使用操作员bool测试有效性。

Account A=GetAccountByID(id);
if (!A)
{
   //whoa there!  that's an invalid account!
}

当我使用数学类型时,我做了很多。例如,我不想编写看起来像这样的函数

bool Matrix_Multiply(a,b,c);

其中a,b和c是矩阵。我更愿意写

c=a*b;

运算符重载。但是有些情况下a和b不能相乘,所以它并不总是有效的。所以他们只是返回一个无效的c,如果它不起作用,我可以做

c=a*b;
if (!c) //handle the problem.

答案 10 :(得分:-1)

boost :: optional可能是你能用语言做的最好的,所以它没有本机变种。