请注意以下内容:
class Foo {
std::string m_bar = "Bar";
int m_baz = 3;
float m_boo = 4.2;
public:
Foo() {}
};
int main() {
Foo f;
f.m_bar = "wunderBar";
}
展开后大致如下:
Foo* fptr = stack_allocate<Foo*>(sizeof(Foo));
fptr->m_bar.string("Bar");
fptr->m_baz.int(3);
fptr->m_boo.float(4.2);
fptr->m_bar.operator=("wunderBar");
出于类似的原因,您可能希望查看C#结构的IL指令 - 您会发现它执行同样冗余的操作(在更复杂的情况下,可能还涉及装箱/拆箱)。
当您合并不可复制或不可移动类型时,您的C++方法也会失败,这将迫使您传递指针和/或弯曲设计。
看起来您正在尝试重新创建Python的可选参数:
class Foo(object):
def __init__(self, Bar, Baz, Boo):
...
class Foo(object):
def __init__(self, Bar="Bar", Baz=13, Boo=4.2):
...
C++没有直接实现这个功能的方法,最接近的机制是默认参数和运算符重载:
class Foo {
std::string m_bar = "Bar";
int m_baz = 3;
float m_boo = 4.2;
public:
Foo(std::string bar="Bar", int baz=3, int boo=6.1)
: m_bar(bar), m_baz(baz), m_boo(boo)
{}
};
或者
class Foo {
std::string m_bar = "Bar";
int m_baz = 3;
float m_boo = 4.2;
public:
Foo() = default;
Foo(const char* bar) : m_bar(bar) {}
Foo(const std::string& bar) : m_bar(bar) {}
Foo(std::string&& bar) : m_bar(std::forward(bar)) {}
explicit Foo(int baz, float boo) : m_baz(baz), m_boo(boo) {}
};
请查看http://ideone.com/yFIqlA以获取SSCE。
如果您真的有十几个不同的构造函数配置,那么您应该重新考虑您的设计。
--- 编辑 ---
注意:您不必在构造函数中公开所有参数:
class Foo {
std::string m_user_supplied;
std::time_t m_time;
public:
Foo() : m_user_supplied(), m_time(0) {}
Foo(std::string src) : m_user_supplied(src), m_time(0) {}
void addTime(time_t inc) { m_time += inc; }
};
--- 编辑 2 ---
"也许应该重新考虑你的设计" ... 大型可选参数列表的一个问题是增长。你可能会得到相互依赖、相互矛盾或相互作用的参数。你可以选择不验证这些参数,或者最终得到复杂的构造函数。
struct Foo {
...
FILE* m_output;
const char* m_mode;
...
Foo(..., FILE* output, const char* mode, ...)
{
...
if (output != nullptr) {
ASSERT( output == nullptr || mode != nullptr );
... other requirements
} else {
if (mode != nullptr)
... might not be an error but it might be a bug ...
}
...
}
};
一种避免这种情况的方法是使用封装/聚合相关成员。
class Foo {
...
struct FileAccess {
FILE* m_file;
const char* m_mode;
constexpr FileAccess() : m_file(nullptr), m_mode(nullptr) noexcept {}
FileAccess(FILE* file, const char* mode) : m_file(file), m_mode(mode) {
if (file == nullptr || mode == nullptr)
throw invalid_argument("file/mode cannot be null");
}
};
...
FileAccess m_access;
Foo(..., FileAccess access, ...);
};
这可以在很大程度上减少膨胀。如果您的API是稳定的,您可以使用它与初始化列表(如果您的API不稳定并且您进行更改将会咬你的屁股)
auto fooWithFile = make_unique<Foo>{..., /*access=*/{stdout, "w"}, ...};
auto fooWithout = make_unique<Foo>{..., /*access=*/{}, ...};
如果您随后决定停止使用构造函数并转而使用设置器,这将翻译得相当好,因为您可以有重载的“set”函数,它接受各种配置结构之一。
auto foo = make_unique<Foo>();
foo->set(FileAccess(stdout, "w"))
->set(Position(Right, -100, Top, 180))
->set(DimensionPercentage(80, 75));
对比
auto foo = make_unique<Foo>() { # pseudo based on if C++ had the C# syntax
m_file = stdout;
m_mode = "w";
m_xPosition = -100;
m_xPositionRel = Right;
m_yPosition = -180;
m_yPositionRel = Top;
m_dimensionType = Percentage;
m_xDimension = 80;
m_yDimension = 75;
};
new
很少使用。 - Jesse GoodC++14
,可以把这个作为工具添加到您的项目中;)。 - Jesse Goodfoo
保留了指针并且之后可以被删除或者删除它,那么这是一个很好的用法,但如果没有,就会导致内存泄漏。 - SHR