【问题标题】:C++ Modifying self via Passed ReferenceC ++通过传递引用修改自我
【发布时间】:2016-06-15 03:18:25
【问题描述】:

我想这是一个最佳实践问题。

在 C++ 中,我有一个用于文件系统路径的包装类,类似于 Python 的 os.path。在这个包装类中,有一个名为“Split”的成员函数,它搜索最后出现的路径分隔符并将其拆分为“尾”(路径的最后部分)和“头”(其他所有部分)。按照目前的情况,该函数使用自己的成员变量 m_filepath 进行拆分。

一些代码:

class FilePath {
    public:
        void Split(FilePath& head, FilePath& tail) const
        {
            FilePath h;
            FilePath t;
            //initialize h, t with portions of m_filepath...
            head = h;
            tail = t;
        }
    private:
        std::string m_filepath;
};

int main(int argc, char ** argv)
{
    FilePath some_path_1("/");
    FilePath some_path_2("/home/");
    some_path_1.Split(some_path_1, some_path_2);
    return 0;
}

当我做这样的事情时,some_path_1 的 m_filepath 将被任何“head”所覆盖。 const 运算符似乎也不介意。

我的问题是,处理这个问题的最佳方法是什么?抛出异常?允许覆盖对象(这让我担心,而且听起来不安全)并告诉开发人员要小心?巧妙利用return语句?

【问题讨论】:

  • 我认为你不需要做任何事情;如果有人写some_path_1.Split(some_path_1, some_path_2);,那么您可以假设他们知道他们正在覆盖some_path_1 的旧内容。在路径操作中,做这种事情并不少见——就地提取路径的一部分,省去了创建另一个变量的麻烦。
  • 只返回一对新路径。它没有什么特别聪明的地方。抛弃输出参数。函数返回其结果。

标签: c++ object reference self mutable


【解决方案1】:

以这种方式编写代码的最大问题是清理代码以使拆分看起来像这样的诱惑:

void Split(FilePath & h, FilePath & t) {
  h.m_filepath = getHead(m_filepath);
  t.m_filepath = getTail(m_filepath);
}

但由于h.m_filepath = ... 实际上正在改变this.m_filepath,第二次调用并没有达到预期的效果。 (请注意,您的代码目前还不错 - 但很脆弱)。

(潜在)问题的根源是使用引用作为返回值和需要多个返回值的组合。但是 C++11 通过tie 支持多个返回值。

所以我将其实现为

class FilePath {
    public:
        std::pair<FilePath,FilePath> Split() const
        {
            FilePath h;
            FilePath t;
            //initialize h, t with portions of m_filepath...
            head = h;
            tail = t;
            return std::make_pair(h,t);
        }
    private:
        std::string m_filepath;
};

那么用法应该是这样的:

int main(int argc, char ** argv)
{
    FilePath some_path_1("/");
    FilePath some_path_2("/home/");
    std::tie(some_path_1, some_path_2) = some_path_1.Split();
    return 0;
}

很明显some_path_1some_path_2 会更新它们的值,您不必担心写入参数会改变这一点,因为不再有参数了。

【讨论】:

  • 这可能是我正在寻找的答案。我不知道 C++11 允许多个返回值。 C++11是我一直在用的(虽然这是一个个人项目,所以我愿意在了解一点后更新到14)。
【解决方案2】:

编辑啊!我刚刚意识到你想让Split() 做什么:将this 拆分为headtail - 并希望他们不要将this 传递给headtail

在另一种情况下,operator =(Class &amp;rhs) 的最佳做法是首先将 &amp;rhsthis 进行比较,以避免确切的问题 - 但我认为您无论如何都不会遇到这个问题。在您给出的用法中,不仅清楚将要发生什么,而且您的实现足以让它发生。我不会担心(正如@M.M 所说)。


当你有一个成员函数时,期望该函数将对类的实例进行操作:即Split() 自身关闭,或者放弃一些关于自身的信息。

你想要的是一个static 成员函数:一个函数,虽然与其类相关联,但不对假定的this 进行操作。使Split()函数static

static void Split(...); // Note no concept of const required - no `this` to modify!

然后这样称呼它:

FilePath::Split(some_path_1, some_path_2);

或者,您可以让Split() 返回尾部,并将自身修改为头部:

FilePath Split();

但在这种情况下,分裂发生的方向有点模糊。怎么样:

FilePath SplitHead();

FilePath SplitTail();

最后两个可以根据上面的static 方法实现,如下所示:

FilePath FilePath::SplitHead() {
    FilePath head = *this;
    Split(head, *this);
    return head;
} // FilePath::SplitHead()

FilePath FilePath::SplitTail() {
    FilePath tail;
    Split(*this, tail);
    return tail;
} // FilePath::SplitTail()

【讨论】:

  • 关于您的第一个答案,我目前没有保持成员变量 m_filepath 静态,它是 FilePath 实例所独有的,并使用构造函数发送的任何内容进行初始化。 Split 使用 m_filepath 进行拆分,并将结果存储在 head 和 tail 中。将成员函数设为静态不需要更改该设计吗?抱歉,如果我对最初的问题含糊不清,我已经更新以防万一。
  • 我没有建议您将 m_filepath 设为静态 - 我很欣赏它应该是 FilePath 的每个实例成员。我建议制作Split() 函数static。这意味着,正如我所描述的,函数本身不绑定到任何特定实例 - 但仍与 FilePath 操作相关联(并且可以访问 private 成员)
  • 我将问题解释为this-&gt;m_filepath 用于Split 函数中,因此它确实需要成为成员
猜你喜欢
  • 2015-05-19
  • 1970-01-01
  • 1970-01-01
  • 2010-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-17
  • 2017-10-21
相关资源
最近更新 更多