
In C++ interviews, “the timing of the default copy constructor generation” is a frequently asked question, this question is often asked in interviews and is also a point that needs special attention in daily coding.It seems simple but can easily lead to pitfalls.
Today, we will gradually break down the basic concepts and interview questions to help you thoroughly understand the key points.
Part1What is a Copy Constructor?
A copy constructor is a special constructor in C++ used to “initialize a new object of the same type with an existing object”. It is the core mechanism for object copying and initialization, and the parameter must be a const reference to an object of the same class (using value passing would trigger recursive calls, resulting in a compilation error). The basic syntax format is: ClassName(const ClassName& sourceObjectName);
1.1, Explicit Definition Example
Taking theStudent class as an example, explicitly implementing the copy constructor to intuitively understand its function:
#include <string>
#include <iostream>
using namespace std;
class Student {
public:
string name; // Custom type member
int age; // Built-in type member
// Explicit copy constructor: member-wise initialization
Student(const Student& other)
: name(other.name), // Call string's copy constructor
age(other.age) // Direct value copy
{
cout << "Student copy constructor called" << endl;
}
// Parameterized constructor (used to create initial object)
Student(const string& n, int a) : name(n), age(a) {}
};
// Calling scenario: initializing a new object with an existing object
int main() {
Student s1("Alice", 20); // Call parameterized constructor
Student s2 = s1; // Call copy constructor, s2 is a copy of s1
Student s3(s1); // Equivalent writing, also calls copy constructor
return 0;
}
Key Point: The copy constructor is not for “copying”, but for “initialization”. It is called when the object is created, not after the object already exists.
1.2, Three Major Calling Scenarios (Must Remember for Interviews)
The copy constructor is not only called during “= initialization”; the following three scenarios will automatically trigger it, which are frequently asked by interviewers:
1. Object Initialization Scenario
Using an already created object to assign a new object, including ClassName newObject = existingObject; and ClassName newObject(existingObject); both forms, as shown in the creation of s2 and s3 above.
2. Function Value Passing Scenario
If the function parameter is a “class object” (not a reference/pointer), calling the function will copy the actual parameter to generate a copy of the parameter, triggering the copy constructor:
void printStudent(Student stu) { // Parameter is an object (value passing)
cout << stu.name << ":" << stu.age << endl;
}
int main() {
Student s1("Bob", 19);
printStudent(s1); // Call copy constructor, generate stu copy
return 0;
}
Note: If the parameter isconst Student&(reference passing), the copy constructor will not be triggered, which is a common optimization for copying.
3. Function Value Return Scenario
If the function return value is a “class object” (not a reference), when returning, a local object will be copied to generate a temporary return value, triggering the copy constructor (note: the compiler may optimize this copy through NRVO, but syntactically it still belongs to the calling scenario):
Student createStudent() {
Student temp("Charlie", 21); // Local object
return temp; // Theoretically calls the copy constructor to generate a temporary object
}
int main() {
Student s = createStudent(); // Receive temporary return value
return 0;
}
Part2Bitwise Copy Semantics and Four Invalid Scenarios
To understand the logic of default copy constructor generation, one must first understand “bitwise copy semantics”—this is the core basis for the compiler to determine whether to generate a default copy constructor.
2.1, What is Bitwise Copy Semantics?
Bitwise copy refers to directly copying the raw bytes of an object in memory without calling any constructor, just like a “photocopier copying a document”, copying memory content byte by byte.
This copying method is efficient and safe, but only applicable to “simple classes without pointers, complex inheritance, or custom copy requirements”, such as:
class SimpleClass {
public:
int num; // Built-in type
double f; // Built-in type
};
int main() {
SimpleClass obj1;
obj1.num = 10;
obj1.f = 3.14;
// Bitwise copy: directly copy obj1's memory bytes to obj2
SimpleClass obj2 = obj1;
// obj2.num = 10, obj2.f = 3.14, no constructor calls
return 0;
}
2.2, Four Scenarios Where Bitwise Copy Semantics Fail
When a class meets any of the following conditions, bitwise copy will lead to errors, and the compiler must generate a default copy constructor to replace bitwise copy:
Scenario 1: Class Contains Members That Require Custom Copy
If the class’s member variables are of “class types with copy constructors” (such asstring or custom classes), bitwise copy will cause the copy logic of member objects to fail.
Example:HasString class containsstring member (string itself has a deep copy constructor):
class HasString {
public:
string str; // string has a custom copy constructor (deep copy)
HasString(const string& s) : str(s) {} // Constructor
};
int main() {
HasString hs1("hello");
// If using bitwise copy: hs1.str and hs2.str will point to the same memory (string's underlying character buffer)
// Actually: the compiler generates a default copy constructor, calling string's copy constructor for deep copy
HasString hs2 = hs1;
return 0;
}
The essence of the problem is that bitwise copy only replicates the memory bytes of the string object (including the pointer to the buffer), but does not trigger the deep copy logic of the string, leading to two string objects sharing the same memory, modifying one will affect the other, and during destruction, memory will be released twice.
Scenario 2: Class Inherits from a Base Class with a Copy Constructor
If the derived class’s base class has an explicit copy constructor, bitwise copy will skip the copy logic of the base class, leading to incomplete initialization of the base class part.
Example:Derived class inheritsBase class (Base has an explicit copy constructor):
class Base {
public:
int val;
Base(int x) : val(x) {}
// Base explicit copy constructor
Base(const Base& other) : val(other.val) {
cout << "Base copy constructor called" << endl;
}
};
class Derived : public Base {
public:
int derVal;
// Derived constructor: calls base class parameterized constructor
Derived(int x, int y) : Base(x), derVal(y) {}
// No explicit copy constructor defined
};
int main() {
Derived d1(10, 20);
// If using bitwise copy: only derVal will be copied, the Base part's val will not call Base's copy constructor
// Actually: the compiler generates a default copy constructor, first calling Base's copy constructor, then copying derVal
Derived d2 = d1;
return 0;
}
Output::Base copy constructor called(proving that the base class copy logic was triggered)
Scenario 3: Class Contains Virtual Functions (or Inherits from a Base Class with Virtual Functions)
Virtual functions rely on the “virtual function table (vtable)” and “virtual function table pointer (vptr)”: each class with virtual functions has a vtable, and each object has a vptr pointing to that vtable. Bitwise copy will cause the vptr to point incorrectly, destroying polymorphic features.
Example:
class Base {
public:
// Virtual function: Base's vtable contains the address of print()
virtual void print() { cout << "This is Base" << endl; }
};
class Derived : public Base {
public:
// Override virtual function: Derived's vtable contains its own print() address
void print() override { cout << "This is Derived" << endl; }
};
int main() {
Derived d; // d's vptr points to Derived's vtable
// If using bitwise copy: b's vptr will be forced to point to Base's vtable (slicing operation + bitwise copy)
// Actually: the compiler generates a default copy constructor, ensuring vptr points correctly (but slicing will still cause polymorphism to fail)
Base b = d;
b.print(); // Outputs "This is Base" (slicing causes polymorphism to fail, unrelated to copy constructor)
return 0;
}
Key Difference:: The default copy constructor is only responsible for “correctly copying the vptr”, but the slicing operation (assigning a derived class object to a base class object) itself will cause polymorphism to fail, which is a syntactic feature unrelated to the copy constructor.
Scenario 4: Inheritance Chain Contains Virtual Base Classes (Solving Diamond Inheritance)
Virtual base classes are used to solve “data redundancy in diamond inheritance”: when a class inherits the same base class from multiple paths, virtual inheritance ensures that the base class has only one instance in the derived class. At this point, the object will contain a “virtual base class pointer (vbptr)” or “virtual base class offset”, which bitwise copy cannot handle correctly.
Example:Classic diamond inheritance (A is a virtual base class):
// Virtual base class: A
class A {
public:
int data;
A(int d) : data(d) {}
};
// Virtual inheritance: B's object contains vbptr pointing to the virtual base class table (records A's offset)
class B : virtual public A {
public:
B(int d) : A(d) {}
};
// Virtual inheritance: C's object contains vbptr
class C : virtual public A {
public:
C(int d) : A(d) {}
};
// Derived class: D inherits B and C, A exists only once in D
class D : public B, public C {
public:
D(int d) : A(d), B(d), C(d) {} // Directly initialize virtual base class A
// No explicit copy constructor defined
};
int main() {
D d1(10); // If using bitwise copy: d2's vbptr cannot correctly point to the virtual base class table, leading to incorrect access of d2.data
// Actually: the compiler generates a default copy constructor, handling vbptr, ensuring A's part is copied only once
D d2 = d1;
cout << d2.data << endl; // Correctly outputs 10
return 0;
}
The principle:The default copy constructor will read the virtual base class table, calculate the offset of the virtual base class A in D, ensuring that d2’s A part is consistent with d1’s, avoiding duplicate copying or out-of-bounds access.
Part3When Does the Compiler Generate a Default Copy Constructor?
This is the core point, remember the core principle: The compiler will only generate a default copy constructor when “not generating it would lead to copy errors”, specifically divided into four situations, each of which can be verified by “checking the obj file” or “assembly code”.
3.1, Situation 1: Class Contains Members That Have Copy Constructors
When the class’s member variables are of “class types with explicit/default copy constructors” (such asstring or custom classes), the compiler will generate a default copy constructor, calling the member object’s copy constructor to complete the copy.
Example:Rectangle class containsPoint member (Point has an explicit copy constructor):
class Point {
public:
int x, y;
Point(int a, int b) : x(a), y(b) {}
// Point's explicit copy constructor
Point(const Point& other) : x(other.x), y(other.y) {
cout << "Point copy constructor called" << endl;
}
};
class Rectangle {
public:
Point topLeft; // Member with copy constructor
Point bottomRight; // Member with copy constructor
// Rectangle's parameterized constructor
Rectangle(int x1, int y1, int x2, int y2)
: topLeft(x1, y1), bottomRight(x2, y2) {}
// Compiler generates default copy constructor: calls both Point's copy constructors
};
int main() {
Rectangle r1(1, 1, 5, 5);
Rectangle r2 = r1; // Triggers default copy constructor
return 0;
}
Output:Point copy constructor called(topLeft copied)
Point copy constructor called(bottomRight copied)
Verification Method: Compile with g++ to generate obj file (g++ -c test.cpp -o test.obj), throughobjdump -d test.obj to view assembly code, you can find that Rectangle’s default copy constructor contains the instruction call Point::Point(const Point&)
3.2, Situation 2: Class Inherits from a Base Class with a Copy Constructor
When the base class of the derived class has an explicit/default copy constructor, the compiler will generate the default copy constructor for the derived class, first calling the base class’s copy constructor, then copying the derived class’s own members.
Example:Dog class inheritsAnimal class (Animal has an explicit copy constructor):
class Animal {
public:
string name;
Animal(const string& n) : name(n) {}
// Base explicit copy constructor
Animal(const Animal& other) : name(other.name) {
cout << "Animal copy constructor called" << endl;
}
};
class Dog : public Animal {
public:
int age;
// Derived class constructor: calls base class parameterized constructor
Dog(const string& n, int a) : Animal(n), age(a) {}
// Compiler generates default copy constructor: first calls Animal's copy constructor, then copies age
};
int main() {
Dog d1("Buddy", 3);
Dog d2 = d1; // Triggers default copy constructor
return 0;
}
Output:Animal copy constructor called
Verification Method: Check the assembly code of the obj file, you can find that Dog’s default copy constructor first executes call Animal::Animal(const Animal&) and then copies the age member (movl %edx, 0x8(%eax) and other instructions).
3.3, Situation 3: Class Contains Virtual Functions (or Inherits from a Base Class with Virtual Functions)
When a class itself has virtual functions, or the inherited base class has virtual functions, the compiler will generate a default copy constructor to ensure that the “virtual function table pointer (vptr)” is correctly copied, maintaining the polymorphic mechanism.
Example:Shape class contains pure virtual functions,Circle class inheritsShape:
class Shape {
public:
virtual void draw() const = 0; // Pure virtual function (class has vtable)
int color;
Shape(int c) : color(c) {}
// Compiler generates default copy constructor: copies color + correctly copies vptr
};
class Circle : public Shape {
public:
int radius;
Circle(int c, int r) : Shape(c), radius(r) {}
void draw() const override { cout << "Drawing Circle" << endl; }
// Compiler generates default copy constructor: calls Shape's default copy, copies radius + copies vptr
};
int main() {
Circle c1(255, 5); // c1's vptr points to Circle's vtable
Circle c2 = c1; // Triggers default copy constructor, c2's vptr also points to Circle's vtable
Shape* p = &c2;
p->draw(); // Outputs "Drawing Circle" (polymorphism works normally)
return 0;
}
Verification Method: Check the assembly code, the default copy constructor will include instructions for “copying vptr” ( such asmovq %rdi, %rax, assigning the original object’s vptr value to the new object).
3.4, Situation 4: Class Inherits Virtual Base Classes (Virtual Inheritance Scenario)
When a class inherits a virtual base class through virtual inheritance (such as in the diamond inheritance scenario), the compiler will generate a default copy constructor to handle the “virtual base class pointer (vbptr)” or “virtual base class offset”, ensuring that the virtual base class part is correctly copied.
Example: Continuing with the previous diamond inheritance code (D class inheritsB andC whereB/C are virtually inheritedA):
int main() {
D d1(10);
D d2 = d1; // Compiler generates default copy constructor, handling vbptr
cout << d2.data << endl; // Correctly outputs 10
return 0;
}
Verification Method: Check the assembly code of the obj file, the default copy constructor will include instructions for “reading the virtual base class table, calculating offsets” ( such asmovq 0x8(%rdi), %rax to get vbptr, then access the virtual base classA data member using the offset).
3.5, Counterexample: When the Compiler Does Not Generate a Default Copy Constructor
If the class only contains “built-in types without copy constructors” ( such asint,double,char), the compiler will not generate a default copy constructor entity and will directly complete the object copy using bitwise copy—because bitwise copy is safe enough, no additional logic is needed.
Example:
class Simple {
public:
int a;
double b;
char c;
};
int main() {
Simple obj1;
obj1.a = 10;
obj1.b = 3.14;
obj1.c = 'A';
Simple obj2 = obj1; // Bitwise copy, no default copy constructor call
return 0;
}
Verification Method: Check the assembly code, there will be no instructions for “calling the copy constructor”, only instructions for “copying memory byte by byte” ( such asmovq %rdi, %rax,movl 0x8(%rdi), 0x8(%rax) and others).
Part4How the Default Copy Constructor Works
To thoroughly understand the default copy constructor, one must break down its working mechanism from the perspectives of “memory operations” and “member type handling differences”.
4.1, Memory-Level Operations: Shallow Copy
The default copy constructor performs a shallow copy, that is:
- Copying byte by byte according to the “storage order of data members in memory”;
- Does not involve copying “external memory pointed to by pointers”, only copies the value of the pointer itself.
Example:
class MyString {
public:
char* str; // Pointer member, points to dynamically allocated memory
MyString(const char* s) {
str = new char[strlen(s) + 1]; // Allocate memory
strcpy(str, s); // Copy string content
}
~MyString() { delete[] str; } // Destructor releases memory
// No explicit copy constructor defined, compiler generates default copy constructor (shallow copy)
};
int main() {
MyString s1("hello");
MyString s2 = s1; // Default copy constructor: shallow copy str pointer
// s1.str and s2.str point to the same memory (0x7ffdf0402a40, example address)
return 0;
}
Memory Diagram:
-
s1:str → 0x7ffdf0402a40 (stores “hello”)
-
s2:str → 0x7ffdf0402a40 (shares memory with s1)
Problem: During destruction,s1 first releases 0x7ffdf0402a40,s2 will trigger a “double free” error when it releases again, causing the program to crash.
4.2, Differences in Handling Built-in Types and Custom Types
The default copy constructor handles different types of member variables differently, which is the core feature of shallow copy:
(1) Built-in Types: Direct Value Copy
For built-in types (int,double,char and pointers, etc.), the default copy constructor directly copies their “values”:
- For int/ double: Copy the numeric value ( like age = other.age);
- For pointers: Copy the address ( like str = other.str), not copying the memory pointed to by the pointer.
Example:
class Test {
public:
int num;
char* ptr;
Test(int n, const char* s) : num(n) {
ptr = new char[strlen(s) + 1];
strcpy(ptr, s);
}
// Default copy constructor: num directly value copies, ptr copies address
};
(2) Custom Types: Calls Their Copy Constructor
For member variables of custom types ( such asstring or Point), the default copy constructor will call the “explicit/default copy constructor” of that member variable to complete the copy:
Example:
class Test {
public:
string str; // Custom type (string has a copy constructor)
Point p; // Custom type (Point has a copy constructor)
Test(const string& s, int x, int y) : str(s), p(x, y) {}
// Default copy constructor: calls str's copy constructor + p's copy constructor
};
Execution Order: Calls the copy constructor in the order of declaration of member variables in the class ( first str and then p).
Part5Three High-Frequency Misconceptions (Easy to Trip Over in Interviews)
Mistake 1: The default copy constructor is “universal” and applicable in all scenarios
Reason for Error:: The default copy constructor is a shallow copy, only applicable to classes “without pointers or dynamic memory”. If the class contains pointers or dynamic resources, it will lead to serious issues.
Counterexample: The previousMyString class ( containing char* pointer), using the default copy constructor will lead to “double free” and “wild pointer”:
int main() {
MyString s1("hello");
MyString s2 = s1; // Shallow copy: s1.str and s2.str point to the same memory
s1.str[0] = 'H'; // Modifying s1's str will also change s2's str (data inconsistency)
return 0; // Destructor will release memory for s2 first, then s1 will release, triggering double free
}
Solution: Custom deep copy constructor, reallocating memory for pointer members and copying the content pointed to by the pointer:
MyString(const MyString& other) {
// Deep copy: allocate independent memory for the new object's str
str = new char[strlen(other.str) + 1];
strcpy(str, other.str); // Copy string content
}
Mistake 2: “If I don’t manually call the copy constructor, it won’t execute”
Reason for Error: The compiler will automatically call the copy constructor in three scenarios: “object initialization, function value passing, function value returning”, even if the code does not explicitly write “= existing object”.
Example 1: Function value passing (implicit call):
void func(MyString s) {} // Value passing
int main() {
MyString s1("test");
func(s1); // Automatically calls copy constructor, generating a copy of s
return 0;
}
Example 2: Implicit writing of object initialization (Test t2(t1)):
class Test {};
int main() {
Test t1;
Test t2(t1); // Implicitly calls copy constructor, no "=" still executes
return 0;
}
Interview Tip: To avoid unnecessary copies, prefer using const ClassName& (reference passing) for function parameters and return values to reduce copy overhead.
Mistake 3: The default copy constructor and the default assignment operator “function the same”
Reason for Error:: The timing and functionality of the two are completely different, and confusion can lead to logical errors in the code.
|
Feature |
Default Copy Constructor |
Default Assignment Operator Overload Function |
|
Timing of Call |
When creating a new object, initializing with an existing object |
Assigning between two existing objects |
|
Functionality |
Initializes a new object, allocates object memory |
Updates the state of an existing object, does not allocate new memory |
|
Syntax Example |
Test t2 = t1; or Test t2(t1); |
Test t1, t2; t2 = t1; |
|
Self-Assignment Handling |
No need to handle (new object cannot be itself) |
Must check for self-assignment (otherwise may lead to double release) |
Example Distinction::
class Number {
public:
int value;
Number(int v) : value(v) {}
// Copy constructor
Number(const Number& other) : value(other.value) {
cout << "Copy constructor called" << endl;
}
// Assignment operator overload function
Number& operator=(const Number& other) {
if (this != &other) { // Self-assignment check
value = other.value;
cout << "Assignment operator called" << endl;
}
return *this;
}
};
int main() {
Number n1(10);
Number n2(n1); // Calls copy constructor (n2 is a new object)
Number n3(20);
n3 = n1; // Calls assignment operator (n3 already exists)
return 0;
}
Output::
-
Copy constructor called
-
Assignment operator called
Part610 High-Frequency Questions (With Complete Answers)
1. When will C++ generate a default copy constructor?
Answer: The compiler will generate a default copy constructor when the user has not explicitly defined a copy constructor and meets any of the following conditions:
① The class contains “class type members with explicit/default copy constructors” ( such asstring or custom classes);
② The class inherits from a “base class with explicit/default copy constructors”;
③ The class itself contains virtual functions, or the inherited base class contains virtual functions;
④ The class inherits a virtual base class through virtual inheritance (such as in diamond inheritance scenarios).
2. If a class only contains member variables of basic data types (such as int, double), will a default copy constructor be generated?
Answer: No default copy constructor entity will be generated. Classes of this type meet bitwise copy semantics, and the compiler will directly complete object copying through “byte-by-byte memory copying”, without needing to generate a copy constructor, achieving the same effect as a copy constructor, but without the constructor call process.
3. What are the behavioral characteristics of the default copy constructor?
Answer: The core characteristic of the default copy constructor is “shallow copy”, specifically manifested as:
① For built-in type members ( such asint and pointers): directly copy values (pointers only copy addresses, not the memory they point to);
② For custom type members ( such asstring or custom classes): call the copy constructor of that member to complete the copy;
③ Execution order: Copy members in the order of declaration in the class, in inheritance scenarios, first copy the base class part, then copy the derived class members.
4. What problems can arise from using the default copy constructor for classes with pointer members?
Answer: Three serious problems can arise:
① Double free error: Two objects’ pointers point to the same memory, and during destruction, the memory will be released twice, causing the program to crash;
② Wild pointer issue: After one object releases memory, the other object’s pointer becomes a wild pointer, and subsequent access to that pointer will trigger undefined behavior (such as program crashes, data corruption);
③ Data consistency issue: Modifying memory content through one object’s pointer will also change the content pointed to by the other object’s pointer, destroying data independence.
5. If a parameterized constructor is defined, will a default copy constructor still be generated?
Answer: Yes, it will be generated. A parameterized constructor only affects the generation of the “default no-argument constructor” (the default no-argument constructor will no longer be generated after defining a parameterized constructor), which is unrelated to the generation logic of the default copy constructor. As long as the user has not explicitly defined a copy constructor and meets the conditions for generating a default copy constructor, the compiler will still generate it.
6. In the context of class inheritance, how does the derived class’s default copy constructor handle the base class part?
Answer: The derived class’s default copy constructor will first call the “base class’s copy constructor” (if the base class has no explicit copy constructor, it will call the base class’s default copy constructor) to complete the copying of the base class part’s members; then, in the order of declaration of the derived class members, it will copy the derived class’s own members (built-in types directly copy, custom types call their copy constructors).
7. If I explicitly do not want the class to use the default copy constructor, what methods are available?
Answer: There are two common methods:
① Before C++11: Declare the copy constructor asprivate and do not provide an implementation. External code calls will trigger a compilation error (cannot access private members), and internal calls will trigger a linking error (no implementation);
② C++11 and later: Use= delete syntax to explicitly delete the copy constructor, formatted as ClassName(const ClassName&) = delete; The compiler will directly disable the copy constructor, and any calls will trigger a compilation error, which is more intuitive than the first method.
8. In the case of virtual inheritance, what special handling does the default copy constructor have?
Answer: In virtual inheritance scenarios, objects will contain a “virtual base class pointer (vbptr)” or “virtual base class offset” to locate the virtual base class instance (to avoid data redundancy). The default copy constructor will perform the following special handling:
① Read the virtual base class table (pointed to by vbptr) and calculate the offset of the virtual base class in the derived class;
② Ensure that the virtual base class part is copied only once (to avoid duplicate copying);
③ Correctly copy the value of vbptr to ensure that the new object can find the virtual base class instance through vbptr, avoiding out-of-bounds access.
9. Does the presence of a move constructor in C++11 affect the logic of generating the default copy constructor?
Answer: No, it does not affect it. The applicable scenarios for move constructors and copy constructors are completely independent:
- Move constructors: Handle right-value objects (such as temporary objects,std::move converted objects), responsible for “resource transfer” (not copying resources, only transferring ownership);
- Copy constructors: Handle left-value objects (such as named objects), responsible for “resource copying”;
As long as the user has not explicitly defined a copy constructor and needs to copy left-value objects, the compiler will still generate a default copy constructor.
10. How to determine if the program has called the default copy constructor?
Answer: There are three common methods:
① Debugging tools tracking: Set breakpoints (such as at object initialization, function parameter passing) in IDEs like VS, Clion, and check the call stack at runtime. If the stack contains “ClassName::ClassName (const ClassName &)” and there is no explicit definition, it is the default copy constructor;
② Check assembly/obj files: Use g++ -S to generate assembly code, or objdump -d test.obj to parse the obj file. If there is an instruction “call ClassName::ClassName (const ClassName &)” and no explicit implementation, it is the default copy constructor;
③ Log assistance: Add output statements in the class’s destructor ( such as cout << “Destructor” << endl;), combined with object copy scenarios (such as function parameters). If the number of destructors is greater than the number of constructors (the extra ones are objects generated by the copy constructor), it can be inferred that the default copy constructor was called.
Previous Recommendations
As expected from Tencent, the quality of the interview is very high
NVIDIA C++ Tegra Interview: What is the underlying principle of mutex?
Kuaishou C++ Second Interview: Analysis of the underlying principle of static_assert
Click below to follow 【Linux Tutorial】 to get programming learning routes, project tutorials, resume templates, large factory interview question PDFs, large factory interview experiences, programming communication circles, etc.