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

What is the benefit of parentheses in that case?

本文关键字:什么 这种情况下      更新时间:2023-10-16

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

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

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

如果没有括号,new Builder()->cup(2)->sugar(3)->flavour("mint")->build();与新表达式的正确语法不匹配:

::(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()不能解释为有效的新表达式。如叮当声:

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

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

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

--实际上是Builder*而不是Tea*。这就是为什么你需要括号,所以首先执行new,然后可以调用你的属性函数并返回一个Tea*

通过括号修复,这会泄漏内存:无法恢复new返回的Builder*值,因此无法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比我回答得更好,更准确。