对我来说,一件简单的事情是模棱两可的。
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;
}
在这种情况下,括号有什么好处?
答案 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的回答比我做的更好,更准确。