什么时候/为什么我会想要显式删除我的构造函数?假设原因是为了防止其使用,为什么不只是将其设为private
?
class Foo
{
public:
Foo() = delete;
};
什么时候/为什么我会想要显式删除我的构造函数?假设原因是为了防止其使用,为什么不只是将其设为private
?
class Foo
{
public:
Foo() = delete;
};
怎么样:
//deleted constructor
class Foo
{
public:
Foo() = delete;
public:
static void foo();
};
void Foo::foo()
{
Foo f; //illegal
}
对抗//private constructor
class Foo
{
private:
Foo() {}
public:
static void foo();
};
void Foo::foo()
{
Foo f; //legal
}
它们基本上是不同的东西。 private
告诉你只有该类的成员(或者友元)才能调用该方法或访问该变量。在这种情况下,该类的静态方法(或任何其他成员)可以调用类的 private
构造函数是合法的。但对于删除的构造函数则不适用。
示例请参见此处。
为什么要显式删除构造函数?
另一个原因:
当我想确保一个类被调用时具有初始化器时,我会使用delete
。我认为这是一种非常优雅的方式,在不进行运行时检查的情况下实现这一点。
C++编译器会为您执行此检查。
class Foo
{
public:
Foo() = delete;
Foo(int bar) : m_bar(bar) {};
private:
int m_bar;
};
Foo foo;
Foo
有许多构造函数,只是没有默认构造函数,那么 Foo foo;
将导致更长的错误列表,列出所有未能匹配的隐式定义、受保护和私有构造函数。 - sigmadelete
操作符总是有效的,并且提供更易于理解的错误信息。不应再使用私有声明来防止调用!
如果将函数声明为私有,则仍然可以调用该函数。例如,在 static
或 friend
函数中,可以调用构造函数。
通过显式删除函数,您表达了它永远不应该被使用。这尤其在尝试调用此类函数时会导致清晰易懂的错误消息。
在下面的类中,我们没有默认构造函数,因为没有有意义的方法来实现它。该类包含一个引用,需要一些对象来指向。
class Foo {
private:
int& ref_;
public:
Foo(int& ref) : ref_(ref) {}
};
int main() {
Foo();
}
$ g++ main.cpp
main.cpp: In function ‘int main()’:
main.cpp:10:9: error: no matching function for call to ‘Foo::Foo()’
10 | Foo();
| ^
main.cpp:6:5: note: candidate: ‘Foo::Foo(int&)’
6 | Foo(int& ref) : ref_(ref) {}
| ^~~
main.cpp:6:5: note: candidate expects 1 argument, 0 provided
main.cpp:1:7: note: candidate: ‘constexpr Foo::Foo(const Foo&)’
1 | class Foo {
| ^~~
main.cpp:1:7: note: candidate expects 1 argument, 0 provided
main.cpp:1:7: note: candidate: ‘constexpr Foo::Foo(Foo&&)’
main.cpp:1:7: note: candidate expects 1 argument, 0 provided
删除构造函数可以使错误信息更简短易懂。
class Foo {
private:
int& ref_;
public:
Foo() = delete;
Foo(int& ref) : ref_(ref) {}
};
int main() {
Foo();
}
$ g++ main.cpp
main.cpp: In function ‘int main()’:
main.cpp:11:9: error: use of deleted function ‘Foo::Foo()’
11 | Foo();
| ^
main.cpp:6:5: note: declared here
6 | Foo() = delete;
| ^~~
class Foo {
private:
int& ref_;
Foo();
public:
Foo(int& ref) : ref_(ref) {}
};
int main() {
Foo();
}
$ g++ main.cpp
main.cpp: In function ‘int main()’:
main.cpp:12:9: error: ‘Foo::Foo()’ is private within this context
12 | Foo();
| ^
main.cpp:5:5: note: declared private here
5 | Foo();
| ^~~
只要你不从类的私有部分可访问的上下文中调用构造函数,这个方法就非常有效。就像上面所写的那样,这可以是一个静态函数或友好函数。原则上,它也可以是普通函数,尽管这种用例相当罕见。
class Foo {
private:
int& ref_;
Foo();
public:
Foo(int& ref) : ref_(ref) {}
static Foo create() {
return Foo(); // compiles fine
}
void foo() {
Foo(); // compiles fine
}
friend void bar();
};
void bar() {
Foo(); // compiles fine
}
int main() {}
g++ -c -o main.o main.cpp
这个程序可以完全编译通过,编译器只是假设 Foo::Foo()
的定义会在其他地方出现。一旦链接器需要将其转换为可执行文件时,它就会报告缺少定义的错误。
$ g++ main.o
# or as one step with compilation and linking
$ g++ main.cpp
/usr/bin/ld: /tmp/ccnhLDsv.o: in function `bar()':
main.cpp:(.text+0x23): undefined reference to `Foo::Foo()'
collect2: error: ld returned 1 exit status
这种错误通常非常难以调试,因为它们可能卡在代码库的任何位置,而您没有线索知道错误出现在哪个文件,更不用说哪一行了。
另一方面,显式的delete
会在三个位置提供三个精确的错误信息,指明错误所在的位置。
class Foo {
private:
int& ref_;
public:
Foo() = delete;
Foo(int& ref) : ref_(ref) {}
static Foo create() {
return Foo(); // error
}
void foo() {
Foo(); // error
}
friend void bar();
};
void bar() {
Foo(); // error
}
int main() {}
$ g++ main.cpp
main.cpp: In static member function ‘static Foo Foo::create()’:
main.cpp:10:20: error: use of deleted function ‘Foo::Foo()’
10 | return Foo(); // error
| ^
main.cpp:6:5: note: declared here
6 | Foo() = delete;
| ^~~
main.cpp: In member function ‘void Foo::foo()’:
main.cpp:14:13: error: use of deleted function ‘Foo::Foo()’
14 | Foo(); // error
| ^
main.cpp:6:5: note: declared here
6 | Foo() = delete;
| ^~~
main.cpp: In function ‘void bar()’:
main.cpp:21:9: error: use of deleted function ‘Foo::Foo()’
21 | Foo(); // error
| ^
main.cpp:6:5: note: declared here
6 | Foo() = delete;
| ^~~
void foo(int need_integer) {}
int main() {
foo(5.4); // might trigger a warning, but compiles
}
delete
也可用于普通函数。例如,可以防止隐式转换。void foo(int need_integer) {}
void foo(double) = delete;
int main() {
foo(5); // okay
foo(5.4); // error
}
$ g++ main.cpp
main.cpp: In function ‘int main()’:
main.cpp:6:8: error: use of deleted function ‘void foo(double)’
6 | foo(5.4); // error
| ~~~^~~~~
main.cpp:2:6: note: declared here
2 | void foo(double) = delete;
| ^~~
= default
和 “Use of deleted function” 搭配得很好,不仅类无法使用它,我个人也更喜欢看到“Use of deleted function”而不是“Function is private”。前者明确表明“这不应该被使用”,如果从中能得到任何东西,那么类无法使用它实际上会产生语义上的差异。 - chris