我已经用谷歌搜索并阅读了很多帖子,但是有很多不同的答案都合乎逻辑,我想知道该主题的专家是否可以揭开这个问题的神秘面纱。
有人说没有返回是因为没有办法返回——语法禁止它——是的,这是有道理的,但我相信所有函数都必须返回一些东西,不是吗?其他人说构造函数会返回新创建的对象本身,这似乎是有道理的,因为在构造函数上使用了赋值运算符。还有一些人有其他有趣的解释。
我已经用谷歌搜索并阅读了很多帖子,但是有很多不同的答案都合乎逻辑,我想知道该主题的专家是否可以揭开这个问题的神秘面纱。
有人说没有返回是因为没有办法返回——语法禁止它——是的,这是有道理的,但我相信所有函数都必须返回一些东西,不是吗?其他人说构造函数会返回新创建的对象本身,这似乎是有道理的,因为在构造函数上使用了赋值运算符。还有一些人有其他有趣的解释。
构造函数不像其他函数那样被调用,所以它们不会像其他函数那样返回。new
它们作为某些构造(强制转换、变量定义、ctor-initializer-list、按值传递、按值返回)的副作用执行。
构造函数没有指定返回类型,因为它是多余的:除了构造函数可能“返回”的类型之外,没有其他类型。我将“return”放在引号中,因为从技术上讲,构造函数不返回任何内容:当在静态上下文中调用它们时,它们会在适当的位置初始化一个实例;当在动态上下文中调用它们时new
,返回的是操作符,而不是构造函数。
构造函数就地构造,它不需要返回任何东西。
我相信所有功能都必须返回一些东西
void
?
假设构造函数可以返回一些东西,那么这对以下函数调用有问题的影响:
class Object{
public:
bool Object(){ ... };
...
}
SomeFun(Object obj){ ... }
SomeFun(Object());
// Ha! SomeFun jokes on an error about a non-Object argument(of type bool), returned
// by the anonymous temporary Object()
防止构造函数返回,便于使用匿名临时对象以及更多 C++ 特性。如果没有这样的规则,无害的陈述可能会变得模棱两可:
Object obj(Object());
// How does the variable obj decide which constructor to call,
// based on its argument type?
//
// Does it call: bool Object::Object(Object&) or
// Object::Object(bool)(if it exists)?
构造函数返回值的能力使对象的创建变得复杂——在没有任意返回值的情况下,只有一个明确的类型、类名可以避免此类问题。
读者能否提出更多示例,说明 C++ 习语因缺少此类规则而受到阻碍?
构造函数缺少返回类型——甚至没有void
——意味着你不能从你的 C++ 代码中调用构造函数。这就是重点。调用构造函数没有意义。将调用构造函数设为非法可以消除此类用户错误的可能性。
编辑
barnes 是对的,你不能调用构造函数的最终原因是它们没有名字。(甚至这忽略了您可以通过放置 new 间接调用构造函数。)
再试一次:
您不能从 C++ 代码中调用构造函数,因为构造函数没有名称。使构造函数没有返回类型向程序员强调构造函数是一种与其他函数非常不同的函数。构造函数实际返回的内容(如果它返回任何内容)取决于供应商。
但我相信所有函数都必须返回一些东西,不是吗?
不,从函数返回值是什么意思?好吧,实现的 ABI 将指定对于某些返回类型,函数将设置某些寄存器,或从堆栈指针的某个偏移量处的某些内存为函数“返回”的值。
显然,寄存器和内存在构造函数或 void 函数被调用后存在并包含数据,但语言说这些函数不返回任何内容,因此 ABI 没有指定要查看哪些寄存器或内存以查找返回值。代码不能设置返回值,调用代码可以得到任何返回值。
所以不,函数不必返回任何东西。
构造函数可能是一个坏名字。它实际上是预构造对象的初始化器。说明这一点的最简单方法是不使用 C++ 构造函数的等效构造的伪示例。
使用构造函数
struct X {
int a;
X() {
a = -5;
}
};
int main() {
X* x1 = new X(); // X created as a "reference object".
X x2; // X created as a "value object" on the stack.
return x1->a - x2.a;
}
没有构造函数
struct X {
int a;
void initialize() {
a = -5;
}
static X* create() {
X* res = (X*)malloc(sizeof(X));
res->initialize();
return res;
}
};
int main() {
X* x1 = X::create(); // New constructed heap-allocated X
X x2; // Stack-allocated X
x2.initialize(); // Manually initialized
return x1->a - x2.a;
}
现在,如果您想象X::initialize
在第二个示例中要返回,例如bool
表示成功或失败,那么您将遇到问题。在main()
中,粗心的程序员可能会得到一个X
未正确初始化的结果,从而导致未定义的行为(通常是在生产之前可能无法发现的难以调试的崩溃)。
这是构造函数变得特别的核心原因之一。退出构造函数的唯一两种方法是通过正常完成或通过异常,然后必须由调用者处理(或向上传递堆栈)。在任何情况下,您都可以防止以未初始化的对象结束。
作为旁注,未初始化的对象是 C 中错误的更常见原因之一,它没有任何东西可以像这样帮助程序员。
构造函数隐式返回类本身的实例。如果它被设计为返回某些东西而程序员返回的东西与类本身完全不同,那将是矛盾的。基本上语法会令人困惑。