在这种情况下,括号有什么好处?

时间:2018-06-23 13:31:07

标签: c++

对我来说,一件简单的事情是模棱两可的。

Tea* mintTea = new Builder()->cup(2)->sugar(3)->flavour("mint")->build();

以前的代码给我一个错误:C2440: 'initializing': cannot convert from 'Builder * to 'Tea *

但是当在new Builder()上加上括号时,代码会很好地工作。

Tea* mintTea = (new Builder())->cup(2)->sugar(3)->flavour("mint")->build();

完整代码:

class Tea;
class Builder {
public:
    Builder() = default;
    ~Builder() = default;
    int m_suger;
    int m_cup;
    string m_flavour;

    Builder* sugar(int sugar);
    Builder* cup(int cup);
    Builder* flavour(string flavour);
    Tea* build();
};
Builder * Builder::sugar(int sugar) {
    this->m_suger = sugar;
    return this;
}
Builder * Builder::cup(int cup) {
    this->m_cup = cup;
    return this;
}
Builder * Builder::flavour(string flavour) {
    this->m_flavour = flavour;
    return this;
}
Tea * Builder::build() {
    return new Tea(this);
}


class Builder;
class Tea {
public: 
    int m_suger;
    int m_cup;
    string m_flavour;

    Tea() = default;
    Tea(Builder* b);
    ~Tea() = default;
};

Tea::Tea(Builder * b) {
    m_suger = b->m_suger;
    m_cup = b->m_cup;
    m_flavour = b->m_flavour;
    cout << "Hot " << b->m_cup << " cup of tea is comming!, with " << b->m_flavour << endl;
}

int main(int argc, char *argv[]) {
    Tea* mintTea = (new Builder())->cup(2)->sugar(3)->flavour("mint")->build();
    return 0;
}

在这种情况下,括号有什么好处?

2 个答案:

答案 0 :(得分:3)

没有括号,new Builder()->cup(2)->sugar(3)->flavour("mint")->build();new expression的正确语法不匹配:

::(optional) new (placement_params)(optional) ( type ) initializer(optional)  (1) 
::(optional) new (placement_params)(optional) type initializer(optional)      (2)

编译器会抱怨它,因为new Builder()->cup(2)->sugar(3)->flavour("mint")->build()不能解释为有效的新表达式。例如clang

prog.cc:56:33: error: expected ';' at end of declaration
    Tea* mintTea = new Builder()->cup(2)->sugar(3)->flavour("mint")->build();
                                ^
                                ;

然后,编译器假定新表达式在new Builder()之后结束,然后给出错误消息:

prog.cc:56:10: error: cannot initialize a variable of type 'Tea *' with an rvalue of type 'Builder *'
    Tea* mintTea = new Builder()->cup(2)->sugar(3)->flavour("mint")->build();
         ^         ~~~~~~~~~~~~~

您的编译器错误消息(即C2440: 'initializing': cannot convert from 'Builder * to 'Tea *)说了同样的话。

使用括号可以很好地工作,因为括号中新表达式的范围受到限制:

  (new Builder())->cup(2)->sugar(3)->flavour("mint")->build();
// ~~~~~~~~~~~~~  the new expression; which returns a Builder*, and all the following code would work fine

答案 1 :(得分:0)

Operator precedence。指针运算符->的成员访问优先于运算符new(不要与operator new()混淆)。因此,表达式的值有些令人困惑:

new Builder()->cup(2)->sugar(3)->flavour("mint")->build()

-实际上是Builder*,而不是Tea*。这就是为什么需要括号的原因,因此首先执行new,然后才能调用属性函数并返回Tea*

使用括号修复后,会泄漏内存:无法恢复Builder*返回的new值,因此也无法恢复delete。每次构建Tea对象时,都会泄漏2个整数和一个字符串。

这两个问题的解决方法在运算符优先级表中:请注意,函数调用的优先级与成员访问相同,并且具有从左到右的关联性。因此,请改为使用静态函数,并确保Builder是可复制的可构造对象,并按值返回:

class Builder {
    public:
        Builder() : m_sugar(0), m_cup(1), m_flavour("earl grey, hot") {}
        static Builder get() { return Builder(); }
        Builder sugar(int sugar) { m_sugar = sugar; return *this; }
        Builder cup(int cup) { m_cup = cup; return *this; }
        Builder flavour(string flavour) { m_flavour = flavour; return *this; }
        Tea* pour() { Tea* hotCuppa = new Tea(this); return hotCuppa; }
    private:
        int m_sugar;
        int m_cup;
        string m_flavour;
};

现在您可以写:

Tea* tea42 = Builder::get().cup(2).sugar(3).flavour("mint").pour();

尽管除非您真的需要指向Tea对象的指针,否则我不会使用工厂模式来构建指针:关于谁拥有该指针会造成混淆。

编辑:@songyuanyao的回答比我做的更好,更准确。

相关问题