1 Introduction
1.1 Function prototype and=delete
In C++11, =delete
there is a new feature that allows us to explicitly prohibit the compiler from automatically generating certain functions. This feature is very useful in many situations, such as when we want to prevent a class from being copied or assigned.
1.1.1 Basic concept of function prototype
Function prototype (Function Prototype) is a form of function declaration, which specifies the name, return type and parameter type list of the function. For example, the following code declares a foo
function named that accepts a int
parameter of type and returns a int
value of type:
int foo(int);
In spoken communication, we usually describe this function prototype like this: "There is a function named 'foo', which takes an integer as its argument and returns an integer." takes an integer as argument and returns an integer.)
1.1.2 Use in function prototypes=delete
In C++11, we can =delete
explicitly prohibit the compiler from automatically generating certain functions by adding it after the function prototype. For example, if we want to prevent a class from being copied, we can do this:
class NonCopyable {
public:
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
};
In this example, we use a class =delete
that disables NonCopyable
the copy constructor and assignment operator. This way, if we try to copy an NonCopyable
object, the compiler will complain.
In spoken communication, we usually describe this code like this: "In the 'NonCopyable' class, the copy constructor and the assignment operator are explicitly deleted to prevent the objects of this class from being copied." , the copy constructor and assignment operator are explicitly removed to prevent objects of this class from being copied.)
1.1.3 =delete
Code examples used
Here's a more complex example that shows how to =delete
suppress certain types of arguments using:
class MyClass {
public:
void process(int value) {
// Process an integer...
}
void process(double value) = delete;
};
In this example, we disabled MyClass::process(double)
the function so that if we tried to double
call the function with a value of a type process
, the compiler would throw an error.
This is a class diagram drawn using PlantUML, which shows MyClass
the structure of the class, including process(int)
functions and deleted process(double)
functions.
2. Function prototype and=delete
2.1 Basic concept of function prototype
In C++, Function Prototype is a way to declare a function. It provides basic information about the function, including the function's name, return type, and parameter list. Function prototype is an important basis for the compiler to perform type checking at compile time.
For example, we have a function prototype as follows:
int add(int a, int b);
This function prototype tells us that add
the function accepts two int
types of parameters and returns a int
type of result.
In spoken communication, we might describe this function prototype like this: "The function takes two integers as arguments and returns an integer. add
"add
In this sentence, "takes ... as arguments" and "returns" are common expressions for describing function prototypes.
2.2 Use in function prototypes=delete
In C++11 and subsequent versions, we can use keywords in function prototypes =delete
, which is a way to explicitly prohibit the use of a function. This technique is often used to disable copying of a class, or to prevent parameters of a specific type.
For example, we have a class NonCopyable
and we don't want it to be copied, then we can define its copy constructor and copy assignment operator like this:
class NonCopyable {
public:
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
};
In this example, =delete
we tell the compiler that we do not want NonCopyable
the copy constructor and copy assignment operator to be used. If we try to copy an NonCopyable
instance, the compiler will complain.
In spoken communication, we might describe this class like this: "The class explicitly deletes its copy constructor and copy assignment NonCopyable
operator, which means it cannot be copied." NonCopyable
, which means it cannot be copied.)
In this sentence, "explicitly deletes" and "cannot be copied" are =delete
common expressions used to describe usage.
In addition, =delete
it can also be used to disable specific types of parameters. For example, we have a function void foo(int)
, but we don't want it to accept double
parameters of type, then we can define it like this:
void foo(double) = delete;
This way, if we try to double
call it with a value of a type foo
, the compiler will throw an error.
In spoken communication, we might describe this function like this: "The function explicitly deletes its overload that takes a double as an argument, which means it cannot be called with a double foo
." foo
float as argument overload, which means it cannot be called with a double precision float.)
In this sentence, "explicitly deletes its overload" (explicitly deletes its overload) and "cannot be called with a double" (cannot be called with a double precision floating point number) are commonly used to describe the use of parameters that prohibit specific types =delete
. way of expression.
In spoken communication, we might describe this class like this: "The class explicitly deletes its copy constructor and copy assignment NonCopyable
operator, which means it cannot be copied." NonCopyable
, which means it cannot be copied.)
In this sentence, "explicitly deletes" and "cannot be copied" are =delete
common expressions used to describe usage.
The use of this technique is discussed in detail in Bjarne Stroustrup's book "The C++ Programming Language". In the book, Stroustrup emphasizes =delete
the importance of improving code security and readability.
The following table summarizes =delete
common uses in function prototypes:
usage | describe |
---|---|
Disable copy constructors | T(const T&) = delete; |
Disable copy assignment operator | T& operator=(const T&) = delete; |
Disable move constructors | T(T&&) = delete; |
Disable move assignment operator | T& operator=(T&&) = delete; |
In the underlying implementation of C++, =delete
this is actually achieved by modifying the access permissions of functions. When we mark a function =delete
as , the compiler sets the access permissions of the function to private
and does not generate an implementation of the function. =delete
This is why the compiler throws an error when we try to call a function.
2.3 Show =delete
code examples used
Let's go through some specific code examples to further understand =delete
its usage.
Example 1: Copying prohibited
class NonCopyable {
public:
NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
};
NonCopyable a;
NonCopyable b(a); // 编译错误:复制构造函数被删除
NonCopyable c;
c = a; // 编译错误:复制赋值运算符被删除
In this example, we passed =delete
the NonCopyable
copy constructor and copy assignment operator disabled, so trying to copy NonCopyable
the instance will result in a compilation error.
Example 2: Disallow specific types of parameters
void foo(int) {
}
void foo(double) = delete;
foo(42); // 正常:调用foo(int)
foo(42.0); // 编译错误:foo(double)被删除
In this example, we pass =delete
prohibition foo(double)
, so trying to double
call it with a value of a type foo
will result in a compilation error.
In spoken communication, we might describe these examples like this: “In the first example, the class NonCopyable
explicitly deletes its copy constructor and copy assignment operator, so trying to copy an instance of NonCopyable
results in a compile error. In the second example, the function foo
explicitly deletes its overload that takes a double, so trying to call foo
with a double results in a compile error." (In the first example, the class NonCopyable
explicitly deleted its copy constructor and copy assignment operator, so trying Copying an NonCopyable
instance of a will result in a compilation error. In the second example, the function foo
explicitly removes its overload that accepts a double-precision floating point number as an argument, so trying to call it with a double-precision floating point number foo
will cause a compilation error.)
3. =delete
Usage scenarios
3.1 Preventing copying of objects
In C++, we sometimes want to prevent object copying to prevent potential errors or unnecessary performance overhead. At this time, we can use =delete
to achieve this purpose.
For example, we have a class NonCopyable
and we don't want its objects to be copied. We can do this:
class NonCopyable {
public:
NonCopyable(const NonCopyable&) = delete; // Copy constructor (复制构造函数)
NonCopyable& operator=(const NonCopyable&) = delete; // Copy assignment operator (复制赋值运算符)
};
In this example, we mark the copy constructor and copy assignment operator as =delete
, which prevents the object from being copied. If we try to copy NonCopyable
the object, the compiler will report an error.
In spoken communication, we can describe this feature like this: "In C++, we can use the delete
keyword to prevent object copying. By marking the copy constructor and copy assignment operator as delete
, we can ensure that objects of the class cannot be copied. If we try to copy an object, the compiler will throw an error." (In C++, we can use keywords to prevent copying of objects. delete
By marking the copy constructor and copy assignment operator as delete
Cannot be copied. If we try to copy an object, the compiler will throw an error.)
In the famous C++ book "Effective C++", the author Scott Meyers also emphasized the importance of this technique. He pointed out: "Making a class noncopyable helps prevent many subtle bugs that can be hard to track down. It's a powerful technique that requires minimal code and yields significant benefits." (Making a class non-copyable can help prevent many subtle bugs that are difficult to track down. It is a powerful technique that requires only minimal code and yields significant benefits.)
The following table summarizes ways to prevent object replication:
method | describe |
---|---|
copy constructor=delete |
Prevent object creation via copy constructor |
copy assignment operator=delete |
Prevent objects from being copied via the copy assignment operator |
This technique can help us write safer and more efficient code and is an important skill in C++.
3.2 Prohibiting certain types of parameters
In C++, we sometimes want a function to only accept parameters of a specific type, and for other types of parameters, we want to prohibit them from being passed in. At this time, we can use it =delete
to achieve this goal.
For example, we have a function void process(Data d)
that can only accept Data
parameters of type. If we wish to disallow other types of parameters, we can do this:
void process(int) = delete;
void process(double) = delete;
void process(std::string) = delete;
// ... and so on for other types
In this example, we mark process
the function's version for types int
, , double
and , thus preventing arguments of these types from being passed in. If we try to call a function with arguments of these types, the compiler will throw an error.std::string
=delete
process
In spoken communication, we can describe this feature like this: "In C++, we can use the delete
keyword to prevent a function from accepting certain types of parameters. By marking the function as delete
for these types, we can ensure that these types of parameters cannot be passed to the function. If we try to call the function with these types of parameters, the compiler will throw an error." (In C++, we can use keywords to prevent a function from accepting certain types of parameters. delete
By passing these By marking function versions of types as delete
, we can ensure that parameters of these types cannot be passed to the function. If we try to call the function with parameters of these types, the compiler will throw an error.)
In the famous C++ book "C++ Primer", the author also emphasized the importance of this technique. He pointed out: "By using, we delete
can make our intentions clear and prevent the compiler from generating code that we do not want." delete
, we can make our intentions clear and prevent the compiler from generating code we don't want.)
The following table summarizes ways to disable certain type parameters:
method | describe |
---|---|
function=delete |
Prevent functions from accepting certain types of arguments |
This technique can help us write safer and more efficient code and is an important skill in C++.
3.3 Prohibit certain special member functions
In C++, the compiler automatically generates some special member functions for each class, such as the default constructor, copy constructor, move constructor, copy assignment operator, and move assignment operator. However, sometimes we may not want the compiler to generate these functions for our class, in which case we can =delete
disable them using .
For example, we have a class NonMovable
and we don't want its objects to be moved. We can do this:
class NonMovable {
public:
NonMovable(NonMovable&&) = delete; // Move constructor (移动构造函数)
NonMovable& operator=(NonMovable&&) = delete; // Move assignment operator (移动赋值运算符)
};
In this example, we mark the move constructor and move assignment operator as =delete
, which prevents the object from being moved. If we try to move NonMovable
the object, the compiler will report an error.
In spoken communication, we can describe this feature like this: "In C++, we can use the delete
keyword to prevent object moving. By marking the move constructor and move assignment operator as delete
, we can ensure that objects of the class cannot be moved. If we try to move an object, the compiler will throw an error." (In C++, we can use keywords to prevent the movement of objects . delete
By marking the move constructor and move assignment operator as delete
Cannot be moved. If we try to move an object, the compiler will throw an error.)
In the famous C++ book "Effective Modern C++", the author Scott Meyers also emphasized the importance of this technique. He pointed out: "By declaring a special member function, you make it clear that this function is not to be used. This can =delete
prevent a lot of headaches caused by the inadvertent use of inappropriate functions." (By declaring a special member function as delete
, you explicitly indicate that the function should not be used. This prevents many problems caused by the inadvertent use of inappropriate functions. )
The following table summarizes ways to disable certain special member functions:
method | describe |
---|---|
special member functions=delete |
Prevent automatic generation of special member functions |
This technique can help us write safer and more efficient code and is an important skill in C++.
4. Precautions
4.1 The difference =delete
between=default
In C++11, =delete
and =default
are two new keywords for controlling the behavior of special member functions. The main difference between them is that =delete
it is used to prohibit the compiler from automatically generating specific functions, but =default
requires the compiler to generate a default function implementation.
4.1.1 =delete
Use
Keywords can be used when we do not want the compiler to automatically generate a certain function for a class =delete
. For example, if we don't want objects of a class to be copied, we can do this:
class NonCopyable {
public:
NonCopyable(const NonCopyable&) = delete; // 删除复制构造函数(Copy Constructor)
NonCopyable& operator=(const NonCopyable&) = delete; // 删除赋值运算符(Assignment Operator)
};
In this example, we removed the copy constructor and assignment operator so that NonCopyable
a copy of the class cannot be created.
4.1.2 =default
Use
=default
The keyword is used to ask the compiler to generate a default function implementation. For example, if we want the default constructor and destructor of a class to have default behavior, we can do this:
class Defaulted {
public:
Defaulted() = default; // 默认构造函数(Default Constructor)
~Defaulted() = default; // 析构函数(Destructor)
};
In this example, we use =default
the keyword to ask the compiler to generate default constructors and destructors for us.
In spoken communication, we can describe the difference between =delete
and like this: " is used when you want to disable the compiler's automatic generation of a certain function, while is used when you want the compiler to generate a default implementation of a function." (" Used when you want to prevent the compiler from automatically generating a function, but used when you want the compiler to generate the default implementation of the function.")=default
=delete
=default
=delete
=default
In this sentence, "is used when" is a common English sentence pattern used to describe the purpose or function of something. "You want to" expresses expectations or goals. This is a common sentence pattern to express wishes or goals.
The following table summarizes the main differences =delete
between =default
:
Keywords | effect | Example |
---|---|---|
=delete |
Disable the compiler from automatically generating specific functions | NonCopyable(const NonCopyable&) = delete; |
=default
| Ask the compiler to generate a default function implementation | Defaulted() = default;
|
In C++ Primer, a classic C++ tutorial, there is also a detailed discussion of =delete
and =default
. The book points out that C++ =delete
and =default
C++ are two new features introduced in C++11. They provide a more refined mechanism to control special member functions, allowing programmers to better control the behavior of classes.
In actual programming practice, we often need to go deep into the underlying source code to understand how these keywords work. For example, when we look at the source code of the C++ standard library, we see that =delete
it =default
is widely used to control the behavior of various containers and algorithms. These source codes provide us with best practices for learning and understanding these keywords.
Overall, C++ =delete
and =default
C++ are two very powerful tools in C++ that can help us write safer, clearer, and more efficient code.
4.2 =delete
Limitations
Although =delete
it is a very powerful tool, we also need to pay attention to some limitations and rules when using it.
4.2.1 Can only be used for member functions of a class
First of all, =delete
it can only be used for member functions of a class, not global functions or non-member functions. For example, the following code is incorrect:
void globalFunction() = delete; // 错误:只能用于类的成员函数
4.2.2 Overloaded functions cannot be deleted
Secondly, we cannot remove overloaded functions. If we try to remove an overloaded function, the compiler will complain. For example, the following code is incorrect:
class MyClass {
public:
void func(int) {
}
void func(double) = delete; // 错误:不能删除重载的函数
};
4.2.3 Virtual functions cannot be deleted
最后,我们不能删除虚函数。如果我们试图删除一个虚函数,编译器会报错。例如,下面的代码是错误的:
class Base {
public:
virtual void func() = 0;
};
class Derived : public Base {
public:
void func() = delete; // 错误:不能删除虚函数
};
在口语交流中,我们可以这样描述=delete
的限制:“=delete
can only be used for member functions of a class, not for global or non-member functions. Also, you can’t delete an overloaded function or a virtual function.”(“=delete
只能用于类的成员函数,不能用于全局函数或非成员函数。而且,你不能删除重载的函数或虚函数。”)
在这个句子中,“can only be used for”(“只能用于”)是一个常见的英语句型,用于描述某个事物的使用范围。“you can’t”(“你不能”)则表示禁止或限制,这是一个常见的表达禁止或限制的句型。
下表总结了=delete
的主要限制:
限制 | 说明 |
---|---|
只能用于类的成员函数 | =delete 不能用于全局函数或非成员函数 |
不能删除重载的函数 | 如果试图删除一个重载的函数,编译器会报错 |
不能删除虚函数 | 如果试图删除一个虚函数,编译器会报错 |
在C++ Primer这本经典的C++教程中,也有对=delete
的限制进行了详细的讨论。书中指出,虽然=delete
是一个非常强大的工具,但是在使用它的时候,我们必须遵
守一些规则和限制,否则可能会导致编译错误。
在实际的编程实践中,我们也需要深入到底层源码中去理解这些限制的原因。例如,当我们查看C++标准库的源码时,我们会发现,虽然=delete
被广泛地用于控制各种容器和算法的行为,但是它们都严格遵守了上述的规则和限制。
总的来说,虽然=delete
有一些限制,但是只要我们正确地使用它,它仍然是一个非常强大的工具,可以帮助我们编写出更安全、更清晰、更高效的代码。
4.3 =delete
与虚函数
虽然在上一节我们提到不能删除虚函数,但是在某些情况下,我们可能会希望禁止派生类覆盖基类的某个虚函数。这时,我们可以使用=delete
来达到这个目的。
4.3.1 删除虚函数的正确方式
在C++中,我们可以通过在基类中将虚函数声明为=delete
来阻止派生类覆盖它。例如:
class Base {
public:
virtual void func() = delete; // 删除虚函数
};
class Derived : public Base {
public:
void func() override; // 错误:不能覆盖已删除的虚函数
};
在这个例子中,我们在基类Base
中将虚函数func
声明为=delete
,这样在派生类Derived
中就不能覆盖这个函数了。
4.3.2 注意事项
需要注意的是,虽然我们可以删除虚函数,但是这并不意味着我们可以在派生类中删除基类的虚函数。如果我们试图在派生类中删除基类的虚函数,编译器会报错。例如,下面的代码是错误的:
class Base {
public:
virtual void func();
};
class Derived : public Base {
public:
void func() override = delete; // 错误:不能在派生类中删除基类的虚函数
};
在口语交流中,我们可以这样描述=delete
与虚函数的关系:“=delete
can be used in the base class to prevent derived classes from overriding a virtual function. However, you can’t delete a base class’s virtual function in a derived class.”(“=delete
可以在基类中用于防止派生类覆盖虚函数。然而,你不能在派生类中删除基类的虚函数。”)
在这个句子中,“can be used in”(“可以在…中使用”)是一个常见的英语句型,用于描述某个事物的使用范围。“you can’t”(“你不能”)则表示禁止或限制,这是一个常见的表达禁止或限制的句型。
总的来说,虽然=delete
在处理虚函数时有一些限制,但是只要我们正确地使用它,它仍然是一个非常强大的工具,可以帮助我们更好地控制类的行为。
5. 为什么使用=delete
5.1 提高代码的安全性
在C++编程中,我们经常会遇到一些情况,其中某些操作可能会导致程序的不正确行为。例如,对于某些类,复制或赋值可能没有意义,或者可能导致错误。在这种情况下,我们可以使用=delete
来显式地禁止这些操作。
考虑以下示例:
class NonCopyable
{
public:
NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete; // 禁止复制
NonCopyable& operator=(const NonCopyable&) = delete; // 禁止赋值
};
在这个例子中,我们创建了一个名为NonCopyable
的类,该类禁止复制和赋值。这样,如果我们试图复制或赋值NonCopyable
的实例,编译器将报错,从而防止可能的错误。
在口语交流中,我们可以这样描述这个特性:“In C++, we can use the delete
keyword to explicitly disable certain operations, such as copy and assignment, for a class. This can help to improve the safety of our code by preventing potential errors.”(在C++中,我们可以使用delete
关键字来显式禁用某个类的某些操作,如复制和赋值。这可以通过防止潜在的错误来提高我们代码的安全性。)
在美式英语中,这个句子的语法结构是完全正确的。主语"We"(我们)后面跟的是可以使用delete
关键字的动作,然后是目的状语从句,解释了我们为什么要这么做。
这种使用=delete
的方法在Bjarne Stroustrup的《The C++ Programming Language》一书中也有详细的讨论。Stroustrup强调,通过禁止不安全或无意义的操作,我们可以使我们的代码更加健壮和安全。
下表总结了使用=delete
与不使用=delete
在代码安全性方面的对比:
使用=delete |
不使用=delete |
---|---|
防止潜在的错误 | 可能导致错误 |
提高代码的健壮性 | 代码可能存在问题 |
显式地禁止某些操作 | 某些操作可能导致不正确的行为 |
通过这种方式,我们可以看到,使用=delete
可以显著提高我们代码的安全性。
5.2 提高代码的可读性
除了提高代码的安全性外,=delete
还可以提高代码的可读性。当我们在代码中看到一个函数被标记为=delete
时,我们可以立即知道这个函数是不可用的,而不需要去查看函数的实现或者文档。
考虑以下示例:
class MyClass
{
public:
void someFunction() = delete;
};
在这个例子中,someFunction
被标记为=delete
,这意味着这个函数是不可用的。这对于阅读代码的人来说是非常清晰的,他们不需要去查看someFunction
的实现或者文档,就可以知道这个函数是不可用的。
在口语交流中,我们可以这样描述这个特性:“In C++, marking a function as delete
can improve the readability of the code. When we see a function marked as delete
, we know immediately that this function is not available, without having to look at the implementation or documentation of the function.”(在C++中,将一个函数标记为delete
可以提高代码的可读性。当我们看到一个函数被标记为delete
时,我们立即就知道这个函数是不可用的,无需查看函数的实现或文档。)
在美式英语中,这个句子的语法结构是完全正确的。主语"We"(我们)后面跟的是看到一个函数被标记为delete
的动作,然后是结果状语从句,解释了我们看到这个标记后的反应。
这种使用=delete
的方法在Scott Meyers的《Effective Modern C++》一书中也有详细的讨论。Meyers强调,通过明确地标记不可用的函数,我们可以使我们的代码更加易读和易理解。
下表总结了使用=delete
与不使用=delete
在代码可读性方面的对比:
使用=delete |
不使用=delete |
---|---|
明确地标记不可用的函数 | 不可用的函数可能不清晰 |
提高代码的易读性 | 代码的可读性可能较低 |
无需查看函数的实现或文档 | 可能需要查看函数的实现或文档 |
通过这种方式,我们可以看到,使用=delete
可以显著提高我们代码的可读性。
5.3 控制类的设计
=delete
不仅可以提高代码的安全性和可读性,还可以帮助我们更好地控制类的设计。我们可以通过=delete
来禁止那些我们不希望在类中出现的操作,从而使我们的类的设计更加清晰和一致。
考虑以下示例:
class Singleton
{
public:
static Singleton& getInstance()
{
static Singleton instance;
return instance;
}
Singleton(const Singleton&) = delete; // 禁止复制
Singleton& operator=(const Singleton&) = delete; // 禁止赋值
private:
Singleton() = default;
};
在这个例子中,我们创建了一个名为Singleton
的类,该类禁止复制和赋值。这样,我们就可以确保Singleton
类的实例只有一个,从而实现了单例模式。
在口语交流中,我们可以这样描述这个特性:“In C++, we can use the delete
keyword to control the design of our classes. By marking certain operations as delete
, we can prevent these operations from being used in our classes, making our class design clearer and more consistent.”(在C++中,我们可以使用delete
关键字来控制我们类的设计。通过将某些操作标记为delete
,我们可以防止这些操作在我们的类中被使用,使我们的类设计更加清晰和一致。)
在美式英语中,这个句子的语法结构是完全正确的。主语"We"(我们)后面跟的是可以使用delete
关键字的动作,然后是结果状语从句,解释了我们为什么要这么做。
这种使用=delete
的方法在Andrei Alexandrescu的《Modern C++ Design》一书中也有详细的讨论。Alexandrescu强调,通过禁止不需要的操作,我们可以使我们的类的设计更加清晰和一致。
下表总结了使用=delete
与不使用=delete
在控制类设计方面的对比:
使用=delete |
不使用=delete |
---|---|
控制类的设计 | 类的设计可能不清晰 |
禁止不需要的操作 | 不需要的操作可能被使用 |
类的设计更加一致 | 类的设计可能不一致 |
通过这种方式,我们可以看到,使用=delete
可以显著帮助我们控制类的设计。
6. 在Qt中的=delete
表现形式
6.1 Qt中的=delete
使用示例
在Qt中,我们经常会看到=delete
的使用,它在Qt中的表现形式主要体现在禁止某些操作,比如禁止复制构造函数和赋值操作。这是因为在Qt的一些类中,复制构造函数和赋值操作可能会导致一些不可预见的问题,比如深浅拷贝问题,资源管理问题等。因此,Qt在设计时就禁止了这些操作。
例如,我们在Qt的QObject
类中就可以看到这样的设计:
Q_DISABLE_COPY(QObject)
Q_DISABLE_COPY(Class)
是一个宏,它的定义如下:
#define Q_DISABLE_COPY(Class) \
Class(const Class &) = delete;\
Class &operator=(const Class &) = delete;
这个宏的作用就是将复制构造函数和赋值操作符声明为=delete
,从而禁止复制和赋值操作。
在口语交流中,我们可以这样描述这个特性:“In Qt, the Q_DISABLE_COPY
macro is used to delete the copy constructor and assignment operator of a class. This prevents the copying and assignment operations, which can help avoid some potential issues such as shallow and deep copy problems, and resource management issues."(在Qt中,Q_DISABLE_COPY
宏用于删除类的复制构造函数和赋值操作符。这可以防止复制和赋值操作,从而帮助避免一些潜在的问题,如浅拷贝和深拷贝问题,以及资源管理问题。)
在这个句子中,“delete the copy constructor and assignment operator of a class”(删除类的复制构造函数和赋值操作符)是一个动宾结构,其中"delete"是动词,表示删除的动作,"the copy constructor and assignment operator of a class"是宾语,表示被删除的对象。“prevent the copying and assignment operations”(防止复制和赋值操作)也是一个动宾结构,其中"prevent"是动词,表示防止的动作,"the copying and assignment operations"是宾语,表示被防止的操作。
在C++的名著《Effective C++》中,Scott Meyers也强调了禁止复制的重要性,他认为如果一个类没有明确地声明它需要进行复制操作,那么就应该禁止复制。
下表总结了=delete
在C++和Qt中的一些常见用法:
用法 | C++ | Qt |
---|---|---|
禁止复制构造函数 | Class(const Class&) = delete; |
Q_DISABLE_COPY(Class) |
禁止赋值操作 | Class& operator=(const Class&) = delete; |
`Q_DISABLE |
_COPY(Class)| | 禁止某种类型的参数 |
void func(int) = delete;| - | | 禁止特殊的成员函数 |
void* operator new(size_t) = delete;` | - |
这些用法在实际编程中非常实用,可以帮助我们更好地控制类的设计,提高代码的安全性和可读性。
6.2 Qt中的=delete
与信号槽机制
在Qt中,=delete
的另一个重要应用是与信号槽机制(Signal-Slot Mechanism)的配合使用。信号槽机制是Qt的一个核心特性,它使得不同对象之间的通信变得简单而直观。
在信号槽机制中,我们经常需要保证槽函数(Slot Function)的唯一性,即一个信号(Signal)只能连接到一个特定的槽函数。这时,我们就可以使用=delete
来禁止复制构造函数和赋值操作,从而保证槽函数的唯一性。
例如,我们可以定义一个槽函数类(Slot Class)如下:
class SlotClass
{
public:
SlotClass() = default;
SlotClass(const SlotClass&) = delete;
SlotClass& operator=(const SlotClass&) = delete;
void slotFunction()
{
// Do something
}
};
在这个类中,我们使用=delete
禁止了复制构造函数和赋值操作,从而保证了slotFunction
的唯一性。这样,我们就可以确保一个信号只能连接到一个特定的slotFunction
。
在口语交流中,我们可以这样描述这个特性:“In Qt, the =delete
keyword can be used in conjunction with the signal-slot mechanism to ensure the uniqueness of slot functions. By deleting the copy constructor and assignment operator of the slot class, we can ensure that a signal can only be connected to a specific slot function."(在Qt中,=delete
关键字可以与信号槽机制结合使用,以确保槽函数的唯一性。通过删除槽类的复制构造函数和赋值操作符,我们可以确保一个信号只能连接到一个特定的槽函数。)
在这个句子中,“ensure the uniqueness of slot functions”(确保槽函数的唯一性)是一个动宾结构,其中"ensure"是动词,表示确保的动作,"the uniqueness of slot functions"是宾语,表示被确保的对象。“deleting the copy constructor and assignment operator of the slot class”(删除槽类的复制构造函数和赋值操作符)也是一个动宾结构,其中"deleting"是动词,表示删除的动作,"the copy constructor and assignment operator of the slot class"是宾语,表示被删除的对象。
在C++的名著《C++ Primer》中,也有类似的观点,作者强调了禁止复制的重要性,并认为这是保证对象唯一性的一种有效方式。
7. 在泛型编程中的=delete运用
7.1. 使用=delete禁止某些模板实例化
在C++11及其以后的版本中,我们可以使用=delete
(删除函数)来显式地阻止编译器为类生成特定的成员函数,或者阻止某些模板实例化。具体来说,template
和 =delete
结合使用,可以让我们在编译时期对模板参数施加条件,从而在模板实例化时进行约束。这是一个强大的工具,用于更深入地控制模板。
当我们声明一个函数为=delete
时,编译器不会生成这个函数的实现,而且试图使用此函数将导致编译错误。此外,我们也可以将一个模板函数指定为=delete
,以防止某些类型的模板实例化。下面的代码展示了一个示例:
template<typename T>
void foo(T t) = delete;
template<>
void foo<int>(int i) {
std::cout << "Specialized for int: " << i << std::endl;
}
在上述代码中,我们使用=delete
禁止了foo
函数模板的所有实例化,然后我们为int
类型提供了特化版本。因此,下面的代码将导致编译错误:
foo("hello"); // error: use of deleted function 'void foo(T) [with T = const char*]'
而下面的代码将正常编译和运行:
foo(10); // outputs: Specialized for int: 10
这种技术是非常有用的,因为它使我们能够根据需要对模板进行更精确的控制。
请注意,在口语交流中,我们可以用以下的方式描述上述情况:“我在foo
函数模板中使用=delete
禁止了所有的模板实例化,但是我为int
类型提供了一个特化版本。”(我禁止了foo
函数模板的所有实例化,但对int
类型提供了一个专门的版本。)
当我们想要更详细地探索这个主题时,我们可以参考 Bjarne Stroustrup 的《C++ Programming Language》中的相关章节。在那里,他详细地解释了模板特化和=delete
的用法,并提供了更多的示例和讨论。
在使用=delete
时,你会发现它是一个强大的工具,它可以帮助你实现你想要的精确控制。这就是为什么在编写复杂的C++代码时,=delete
是不可或缺的一部分。
7.2. 使用=delete控制模板特化
在C++泛型编程中,template
允许我们编写可以处理多种数据类型的代码,而模板特化允许我们为某种特定类型提供特殊的实现。这为我们提供了巨大的灵活性,但有时我们可能希望阻止某些类型的特化。这就是=delete
发挥作用的地方。
首先,我们需要理解一种常见的误解,那就是=delete
不能直接用于类模板的特化。换句话说,我们不能这样写:
template<>
class MyTemplate<char> = delete; // Error: deletion of specialization of 'MyTemplate<char>'
这样的语法是不允许的。然而,我们可以通过删除类模板特化中的特定函数来达到类似的效果。例如:
template<typename T>
class MyTemplate {
public:
void func() {
// common implementation
}
};
template<>
class MyTemplate<char> {
public:
void func() = delete; // Prevent usage with char
};
In this example, we provide a definition for a specialization char
of the type , but we use the delete function. Therefore, trying to use a function of the type will result in a compilation error.MyTemplate
=delete
func
char
MyTemplate
func
In spoken communication, we might describe the above situation like this: "I specialize a MyTemplate<char>
and =delete
disable func
the use of the function." (I create a MyTemplate<char>
specialization of a and disable the use of func
the function.)
=delete
This allows us to take advantage of finer control in template specialization . For a more in-depth understanding, we can refer to the book "C++ Templates: The Complete Guide", which provides an in-depth discussion of template specialization and its =delete
use in template specialization.
Additionally, you may be wondering how to summarize this information in a markdown table. Here is a simple example:
technology | describe | example |
---|---|---|
template |
Write code that can handle multiple types | template<typename T> |
template specialization | Provide special implementations for specific types | template<> class MyTemplate<int> |
=delete |
Explicitly block functions or specializations | void func() = delete |
In general, =delete
the role of generic programming in C++ is not limited to preventing template instantiation, but can also be used to prevent specific functions in template specialization, allowing us to control the behavior of the code more finely.