Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/134.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/visual-studio/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 移动语义与虚拟方法_C++_C++11_Move Semantics_Pure Virtual - Fatal编程技术网

C++ 移动语义与虚拟方法

C++ 移动语义与虚拟方法,c++,c++11,move-semantics,pure-virtual,C++,C++11,Move Semantics,Pure Virtual,在C++11中,我们在某些情况下被引导通过值传递对象,在另一些情况下通过常量引用传递对象。但是,本指南取决于方法的实现,而不仅仅取决于其接口和客户的预期用途 当我编写接口时,我不知道它将如何实现。写方法签名有好的经验法则吗?例如-在下面的代码片段中,我应该使用Bar1还是Bar2 class IFoo { public: virtual void Bar1(std::string s) = 0; virtual void Bar2(const std::string& s

在C++11中,我们在某些情况下被引导通过值传递对象,在另一些情况下通过常量引用传递对象。但是,本指南取决于方法的实现,而不仅仅取决于其接口和客户的预期用途

当我编写接口时,我不知道它将如何实现。写方法签名有好的经验法则吗?例如-在下面的代码片段中,我应该使用
Bar1
还是
Bar2

class IFoo
{
public:
    virtual void Bar1(std::string s) = 0;
    virtual void Bar2(const std::string& s) = 0;
};
如果您同意正确的签名取决于实现,则可以停止阅读此处。下面是一个例子,说明我为什么这么认为

在以下示例中,我们应按值传递字符串:

class Foo
{
    std::string bar;

    Foo(std::string byValue)
        : bar(std::move(byValue))
    {
    }
};
现在,我们可以在所有情况下以有效的方式实例化Foo:

Foo foo1("Hello world"); // create once, move once
Foo foo2(s); // the programmer wants to copy s. One copy and one move
Foo foo3(std::move(t)); // the programmer does not need t anymore. No copy at all
在其他情况下,我们更喜欢通过常量引用传递对象。例如,在以下情况下,我们不想复制/存储参数,只需使用其方法即可:

void DoStuff(const std::string& byRef)
{
    std::cout << byRef.length() << std::endl;
}
然后我们将得到以下结果:

Foo foo1("Hello world"); // Here we would have one more copy of the string. It is less efficient.
Foo foo2(s);             // One copy, like before
Foo foo3(std::move(t));  // Irrelevant here.
亚历克斯。

这里没有“万物理论”。你说得对,有个问题。 我记得不久前我自己也面对过

我的结论从这里开始:

应用程序与框架/库开发 如果您的客户是开发人员,那么这项工作就要困难得多。这不仅更加困难,而且没有明确的指导方针。伟大的框架设计师获得了他们的威望,因为他们碰巧冒了有回报的风险。与此同时,在另一个宇宙中,他们的风险可能不会得到回报。这是因为对一个框架的评价取决于它不断增长的使用方向,以及比在应用领域更难推理的主观观点

所以在这种情况下没有明确的答案。幸运的是,我认为您主要对这里的应用程序开发感兴趣。让我们开始吧

起点:我们正在开发应用程序 这是一个巨大的不同。因为我们应该更好地了解系统的发展方向,以及什么样的代码才是有用的。我们不是预言家,但同时这一假设让我们更加相信我们的直觉,这是基于我们对需求和客户需求的了解(至少是我们能够理解的)

此时,我们仍然可以将其分为两种情况:

从抽象到实现 在有些情况下,在实现之前定义抽象是有益的,甚至是必要的。在这种情况下,我们必须意识到,在正确定义抽象之前,需要对问题进行更多的研究。例如,域是同步的还是异步的?串行还是并行?高水平还是低水平?以及其他更具体的问题

一些极度敏捷的人会让你相信,你只需编写一些代码,然后再进行修复。然而,一旦现实发生,这种说法很容易被篡改。如果你从中找到希望,我鼓励你自己测试它,如果你有任何重大发现,就报告。我个人的经验,以及我尝试过的想法表明,在大型项目中,这种方法是非常有问题的

本例的结论是,如果您确实需要提前定义抽象,那么您应该已经对实现有了很好的了解。你对它的想法越好,它就越有可能成为一个正确的抽象概念

抽象的实现 这是我的默认选择。有很多种说法。“框架应该被提取”,“提取”直到你放弃”,甚至“约定优先于配置”在概念上也有一些相似之处

基本上,这意味着您可以根据需要实现所需的组件,但要密切关注正在发生的事情。这里的诀窍是寻找机会,以在开发和维护方面实际有益于您的方式进行抽象

这通常是一个做你想做的事情的类,但更多。在这种情况下,将交叉点抽象为更一般的情况。在整个开发过程中,您可以根据需要重复此过程

重要的是不要被追赶,而且要脚踏实地。我已经看到许多抽象尝试出错,以至于除了阅读数千行使用它的代码外,无法对其名称进行推理并推断其意图。例如,在我正在处理的当前代码库中,应该被称为
Image
的类型被称为
BinaryData
。所有代码都试图将其视为一个具体的(图像),同时也是一个抽象的概念

总结
正如我经常提醒自己的那样,你可以拥有的最佳实践是驯服已知的最佳实践以适应你的问题,而不是反过来。如果你做不到这一点,那么,也许这个问题很有趣,需要进一步的关注和一点独创性的思考。

我认为它肯定取决于实现。正如您的问题所暗示的,除非使用完全“总是更好”的签名,否则唯一明智的做法是以优化当前实现的方式选择签名。如果您在编写代码之前就编写了接口,那么请进行有根据的猜测,并尝试以这样一种方式操纵自己,即您可以在提交签名之前等待第一个实现

这里的关键词是“第一”和“当前”。如果你搞错了怎么办?如果在以后的某个阶段签名阻止了代码的优化,会发生什么?以下是您可以做的:

没有承诺 如果足够快的话,就改变它。它来自于“没有承诺”的定义,对吗

致力于API 对于一个具体的例子,假设您选择了错误的选项,并使用以下选项:

virtualvoiddostuff(std::string s)=0;
然而,正如它所说
Foo foo1("Hello world"); // Here we would have one more copy of the string. It is less efficient.
Foo foo2(s);             // One copy, like before
Foo foo3(std::move(t));  // Irrelevant here.
class IFoo
{
public:
    virtual void Bar2(const std::string& s) = 0;

    virtual void Bar2(std::string&& s)
    {
        Bar2(s);   // calls the const& overload because s is an lvalue
    }
};