是否可以使用对象“填充”函数参数(默认为null)?

时间:2016-03-20 16:43:08

标签: c++ pass-by-reference optional-parameters nullptr

说我有这个方法:

bool match( /* some optional parameter */ );

将执行一些字符串模式匹配,并且我希望允许给它一个可选参数,当方法Match返回时,该参数只会填充已知类型的实例(match()true,这样的事情会有可能吗?

在PHP中,我可以这样做:

public function match( Match &$match = null ) {
  if( someMatchingRoutineMatched() ) {
    $match = new Match();
    return true;
  }

  return false; // $match will stay null
}

然后这样称呼它:

// $test is some instance of the class that implements match()
// I don't have to declare $m up front, since it will be filled by reference
if( $test->match( $m ) ) {
  // $m would be filled with an instance of Match
}
else {
  // $m would be null
}

c ++中有类似的东西吗?

我有点使用以下

bool match( Match*& match ) {
  if( /* something matches */ ) {
    match = new Match;
    return true;
  }

  return false;
}

...然后像这样调用它:

Match* m = nullptr; // I wish I wouldn't have to declare this upfront as a nullptr
if( test.match( m ) ) {
  // a match occured, so m should no longer be a null pointer
  // but let's just make sure
  if( m != nullptr ) {
    // do something useful with m and afterwards delete it
    delete m;
  }
}

...然而,这一切都感觉有点麻烦。此外,我似乎不允许使参数可选,如:

bool match( Match*& match = nullptr );

...因为,我相信,引用不允许为空,对吗?

我希望你能看到我想要实现的目标,并且你可以对如何实现我的目标提供一些见解,如果它可以开始的那样,那就是。

4 个答案:

答案 0 :(得分:4)

  

“Match *&”类型的非const引用的无效初始化来自'匹配*'

类型的右值

Match*& match = nullptr是不允许的,因为对非const的引用无法绑定到临时对象,并且在此处传递nullptr会创建一个临时的Match*

您可以返回指针而不是传递引用到非const:

Match* match() {
  if( /* something matches */ ) {
    return new Match;
  }

  return nullptr;
}

现在nullptr返回值表示没有匹配,非nullptr表示找到了匹配项:

if( Match* m = test.match() ) { // nullptr means false, non-nullptr means true
  if( m != nullptr ) { // always true here
    // do something useful with m and afterwards delete it
    delete m;
  }
}

或者你可以像@DanMašek提到的那样使用重载:

bool match() {
  Match* m = nullptr;
  bool result = match(m);
  delete m; // deleting a nullptr is a no-op
  return result;
}

最后但绝对不是最不重要的,强制使用 - unique_ptr - 过度拥有指针,因此您不必担心delete,而且如果不阅读match的文档就很清楚{1}}返回的指针是拥有还是不拥有:

unique_ptr<Match> match() {
  if( /* something matches */ ) {
    return make_unique<Match>( /* constructor arguments */ );
  }

  return nullptr;
}

if( auto m = test.match() ) { // m deduced to be of type unique_ptr<Match>
  if( m != nullptr ) { // always true here
    // do something useful with m and afterwards delete it
    // no need to delete explicitly
  }
}

答案 1 :(得分:3)

指针指针,指针引用或可选&#34; out&#34;在C ++中,参数都是C风格的编码和/或根本不是非常惯用的。

如果不明确需要,也不会使用动态分配(new Match)。

更像C ++的问题解决方案将是boost::optional,这是一种C ++习惯用法的实现,有时被称为“#34; Fallible&#34;”。我们的想法是返回一个带有两个成员变量的对象,一个是信号成功,另一个是&#34;真正的&#34;如果第一个为真,则返回值。

您也可以自己实现一个基本版本,当我们使用它时,将指针替换为普通实例,因为Match听起来不像一个需要动态分配实例的类< SUP> [*]

struct Result
{
    bool success;
    Match match;
};

更精细的实现会将此变为具有私有成员变量和公共成员函数的类,确保在match为false时访问success失败:

class Result
{
public:
    bool success() const { return m_success; }
    Match match() const {
        assert(m_success);
        return m_match;
    }

    Result() : m_success(false), m_match() {}
    Result(Match const& match) : m_success(true), m_match(match) {}

private:
    bool m_success;
    Match m_match;
};

match()函数本身将如下所示:

Result match()
{
    if (someMatchingRoutineMatched()) {
        return Result(Match(some_match_arguments));
    }

    return Result();
}

然后调用者会像这样使用它:

auto const result = match();
if (result.success())
{
    // use result.match()
}

如果您向班级添加bool转换运算符,即:

    explicit operator bool() const
    {
        return m_success;
    }

然后您也可以按如下方式编写if,使其看起来更像您原来的PHP代码,虽然我不认为它更具可读性,我建议不要它:

if (auto const result = match()) {
    // use result.match()
} else {
    // access to result.match() neither allowed nor needed
}

[*]如果确实需要动态分配,例如因为Match是多态类层次结构的一部分,那么请考虑std::unique_ptr

答案 2 :(得分:1)

如果@zenith提出错误,我非常喜欢返回nullptr的c / c ++样式。

但是对于记录,这里有另一种选择:

bool match( Match*& match ) {
  bool some_condition=true; 
  if( some_condition ) {
    match = new Match;
    return true;
  }
  match = nullptr;   // so that you don't need to set it upfront
  return false;
}

bool match() {       // here if you don't have any pointer to provide
    Match *forget_it; 
    return match(forget_it);
}

但是,有一个弱点,因为如果你总是在免费商店中创建一个新的匹配,你就会泄漏内存。所以匿名版本必须更新:

bool match() {
    Match *forget_it; 
    bool rc=match(forget_it);
    delete forget_it;  // avoid leaking memory
    return rc;
}

不幸的是,仍然可能来自普通Match()的调用者需要释放内存。所以我建议使用unique_ptr<Match>shared_ptr<Match>而不是原始指针。

答案 3 :(得分:1)

处理此问题的首选方法可能是,如果match成功,则返回指向已创建对象的指针,否则返回nullptr,如天顶所示。但是如果你想要一些类似于你在PHP中做的事情,我建议如下:

#include <memory>

bool match(std::unique_ptr<Match>& ptr)
{
    bool some_condition = true;
    if(some_condition) {
        ptr = std::make_unique<Match>();
        return true;
    }
    else
        return false;
}

bool match()
{
    std::unique_ptr<Match> ptr;
    return match(ptr);
}

int main()
{
   match(); // if Match is created, it's automatically discarded
   std::unique_ptr<Match> ptr;
   if(match(ptr))
   {
       // do stuff with ptr
       // ptr will be deleted once it goes out of scope
   }

   return 0;
}