我使用命名的构造函数idiom来创建对象,因为我有很多具有相同参数的调用但是对象的创建方式不同。
C++ FAQ告诉我们如何做到这一点。它还告诉我们如何强制对象进行堆分配。但它确实没有告诉我们如何使用new运算符来命名构造函数。
因为new需要调用构造函数,所以我们无法直接调用命名构造函数。所以我找到了两个解决这个问题的方法:
我创建了一个额外的复制构造函数,并希望优化编译器不会创建临时对象。
class point_t {
int X,Y;
point_t(int x, int y) : X(x), Y(y) { }
public:
point_t(const point_t &x) : X(x.X), Y(x.Y) { }
static point_t carthesian(int x, int y) { return point_t(x,y); }
static point_t polar(float radius, float angle) {
return point_t(radius*std::cos(angle), radius*std::sin(angle));
}
void add(int x, int y) { X += x; Y += y; }
};
int main(int argc, char **argv) {
/* XXX: hope that compiler doesn't create a temporary */
point_t *x = new point_t(point_t::carthesian(1,2));
x->add(1,2);
}
另一个版本是创建单独的命名构造函数。因为函数重载在返回类型上不起作用,所以使用两个不同的名称,这很难看。
class point_t {
int X,Y;
point_t(int x, int y) : X(x), Y(y) { }
public:
/* XXX: function overloading doesn't work on return types */
static point_t carthesian(int x, int y) { return point_t(x,y); }
static point_t *carthesian_heap(int x, int y) { return new point_t(x,y); }
void add(int x, int y) { X += x; Y += y; }
};
int main(int argc, char **argv) {
point_t *x = point_t::carthesian_heap(1,2);
x->add(1,2);
}
是否有一个更漂亮的版本等于示例代码?
答案 0 :(得分:13)
你可以完全避免使用命名构造函数idiom,并使用additonal dummy enum参数来选择构造函数。
enum Carthesian {carthesian};
enum Polar {polar};
class point_t {
int X,Y;
public:
point_t(int x, int y) : X(x), Y(y) { } // may keep as a default
point_t(Carthesian, int x, int y) :X(x),Y(y){}
point_t(Polar, float radius, float angle)
: X (radius*std::cos(angle)), Y(radius*std::sin(angle)) {}
void add(int x, int y) { X += x; Y += y; }
};
int main(int argc, char **argv) {
point_t *x = new point_t(carthesian,1,2);
point_t *y = new point_t(polar,0,3);
x->add(1,2);
}
它简单,便携,您将看到的唯一开销是传递虚拟枚举值。在极少数情况下,这种开销对您来说太高了,即使构造本身没有内联,也可以通过包装函数调用来消除它,如下所示:
enum Carthesian {carthesian};
enum Polar {polar};
class point_t {
int X,Y;
void initCarthesian(int x, int y); // may be long, not inlined
void initPolar(float radius, float angle);
public:
point_t(int x, int y) : X(x), Y(y) { } // may keep as a default
point_t(Carthesian, int x, int y)
{initCarthesian(x,y);} // this is short and inlined
point_t(Polar, float radius, float angle) {initPolar(radius, angle);}
void add(int x, int y) { X += x; Y += y; }
};
另一种方法是使用派生类进行构造。使用内部类时,我认为它会产生一个很好的语法:
class point_t {
int X,Y;
public:
struct carthesian;
struct polar;
point_t(int x, int y) : X(x), Y(y) { } // may keep as a default
void add(int x, int y) { X += x; Y += y; }
};
struct point_t::carthesian: public point_t
{
carthesian(int x, int y):point_t(x,y){}
};
struct point_t::polar: public point_t
{
polar(float radius, float angle):point_t(radius*std::cos(angle),radius*std::sin(angle)){}
};
int main(int argc, char **argv) {
point_t *x = new point_t::carthesian(1,2);
point_t *y = new point_t::polar(0,3);
x->add(1,2);
return 0;
}
答案 1 :(得分:8)
你可以写:
point_t *x = new point_t(point_t::carthesian(1,2));
首先调用carthesian()
然后调用复制构造函数。
或者,它有什么问题吗?也许,有点慢?
顺便说一句,这段代码有一个明显的优势:程序员可以在他的代码中清楚地看到new
运算符(他使用其他人撰写的point_t
,因此您可以假设他的责任在完成delete
后致电x
。
答案 2 :(得分:4)
这真的是个问题吗?根据我的经验,课程往往是大部分时间动态分配,或者很少,如果有的话。表示值的类(例如此处的point_t类)属于第二类,而表示实体的类(即具有标识的类)属于第一类。
所以我的建议是选择你认为每个班级最好的方法并且只提供它。请注意,您始终可以返回一个小的直接分配的对象,该对象具有指向较大的对象的私有指针,如the Handle-Body idiom中所示。
另一方面,其他答案显示了如何消除带有相同数量和类型参数的构造函数的歧义。在这种思路中,一种替代方法是为参数引入特定类型,如下所示:
class radius_t {
float R;
public:
explicit radius_t(float r) : R(r) {}
operator float() const { return R; }
};
class angle_t {
float A;
public:
explicit angle_t(float a) : A(a) {}
operator float() const { return A; }
};
class point_t {
float X,Y;
public:
point_t(float x, float y) : X(x), Y(y) { }
point_t(radius_t radius, angle_t angle) :
X(radius*std::cos(angle)), Y((radius*std::sin(angle)) {
}
void add(int x, int y) { X += x; Y += y; }
};
int main(int argc, char **argv) {
point_t *x = new point_t(radius_t(1),angle_t(2));
x->add(1,2);
}
答案 3 :(得分:0)
我还没有看到的一种方法是重载构造函数,使得堆分配使用最后一个参数作为out(假设第二个函数在技术上不是构造函数,它不会返回一个实例)。结果就像(作为你的第二个代码片段的基础):
class point_t {
int X,Y;
point_t(int x, int y) : X(x), Y(y) { }
public:
/* XXX: function overloading doesn't work on return types */
static point_t carthesian(const int x, const int y) { return point_t(x,y); }
static void carthesian(const int x, const int y, point_t * & point) { point = new point_t(x,y); }
void add(int x, int y) { X += x; Y += y; }
void add(const point_t & point) { this->X += point.x; this->Y += point.y; }
};
int main(int argc, char **argv) {
point_t p1 = point_t::carthesion(1, 2);
point_t * p2;
point_t::carthesian(1, 2, p2);
p2->add(p1);
}
答案 4 :(得分:-1)
可以想到template
分配器:
template<typename T>
struct Allocator : T
{
template<typename A1, typename A2>
Allocator(A1 a1, A2 a2) : T(a1, a2) {}
};
class point_t {
//...
template<typename T> friend struct Allocator;
};
int main(int argc, char **argv) {
point_t *x = new Allocator<point_t>(1,2);
x->add(1,2);
}
现在Allocator
是friend
的{{1}}。所以它可以访问它的point_t
构造函数。此外,您可以在private
中添加更多构造函数,例如<A1, A2>
,以使其更加generalized。优点是:
Allocator
船只未被利用,friend
是Allocator
我们仅将其用于堆分配