当我们定义一个C++类的时候,编译器会默认为我们生成六个成员函数,分别是默认构造函数、析构函数、拷贝构造函数、拷贝赋值操作符、移动构造函数和移动赋值操作符。这些成员函数可以帮助我们管理内存和类对象的创建、销毁、拷贝和赋值等操作,同时也会影响到对象的生命周期和程序的效率。因此,我们需要深入了解这六个函数的作用和实现机制,才能写出高效、健壮的代码。
默认构造函数
默认构造函数是一个没有参数的构造函数,它会被自动调用来创建一个类的对象。如果我们没有手动定义一个构造函数,编译器会自动生成一个默认构造函数。它的主要作用是初始化类的成员变量,为它们分配内存空间和设置默认值。默认构造函数的定义方法如下:
class ClassName {
public:
ClassName(); //默认构造函数
//其他成员函数和变量
};
当我们创建一个没有参数的类对象时,编译器就会自动调用默认构造函数。例如:
ClassName obj; //调用默认构造函数
在实际开发中,我们可以根据需要重载默认构造函数,在其中执行初始化代码,完成更多的自定义操作。例如:
class Person {
public:
Person() : name("Unknown"), age(0) {}
Person(string n, int a) : name(n), age(a) {}
//其他代码和成员变量
private:
string name;
int age;
};
这里我们定义了一个Person类,它有两个构造函数,一个是默认构造函数,一个是带参数的构造函数。在默认构造函数中,我们把姓名初始化为"Unknown",年龄初始化为0。
析构函数
析构函数是一个没有参数的函数,它会在对象生命期结束时自动调用,用来释放对象在堆栈或者堆中分配的资源。如果我们没有手动定义一个析构函数,编译器也会自动生成一个默认析构函数。析构函数的定义方法如下:
class ClassName {
public:
~ClassName(); //析构函数
//其他成员函数和变量
};
例如,在Person类的析构函数中,我们可以释放对象占用的内存空间,如下所示:
class Person {
public:
~Person() {
delete[] name;
}
//其他代码和成员变量
private:
char *name;
};
在这个例子中,我们使用了new运算符为字符指针name分配了一块内存空间,要在对象被销毁时释放它。因此,我们在析构函数中使用了delete[]运算符来释放内存。
拷贝构造函数
拷贝构造函数是一种特殊的构造函数,它是用来创建一个对象的副本的。当我们使用一个对象来初始化另一个对象时,拷贝构造函数就会被调用。例如:
ClassName obj1; //调用默认构造函数
ClassName obj2 = obj1; //调用拷贝构造函数
ClassName obj3(obj1); //调用拷贝构造函数
拷贝构造函数的定义方法如下:
class ClassName {
public:
ClassName(const ClassName & other); //拷贝构造函数
//其他成员函数和变量
};
我们需要在拷贝构造函数中复制参数对象的成员变量,并为新对象分配新的内存空间,以避免两个对象之间的相互干扰。例如:
class String {
public:
String() : data(nullptr), len(0) {}
String(const char* str) {
len = strlen(str);
if (len > 0) {
data = new char[len + 1];
strncpy(data, str, len + 1);
} else {
data = nullptr;
len = 0;
}
}
String(const String& other) {
len = other.len;
if (len > 0) {
data = new char[len + 1];
strncpy(data, other.data, len + 1);
} else {
data = nullptr;
len = 0;
}
}
~String() {delete[] data;}
//其他代码和成员变量
private:
char* data;
size_t len;
};
在这个例子中,我们定义了一个String类,它有三个成员函数:默认构造函数、带参数的构造函数和拷贝构造函数。我们在拷贝构造函数中使用了new运算符为新的对象分配了内存空间,并调用了strncpy函数来复制原字符串到新的内存空间中,以保证两个对象之间的内存地址不会冲突。
拷贝赋值操作符
拷贝赋值操作符是用来将一个对象赋值给另一个对象的。例如:
ClassName obj1, obj2; //调用默认构造函数
obj2 = obj1; //调用拷贝赋值操作符
拷贝赋值操作符的定义方法如下:
class ClassName {
public:
ClassName& operator=(const ClassName & other); //拷贝赋值操作符
//其他成员函数和变量
};
在拷贝赋值操作符中,我们需要检查被赋值对象和参数对象是否相同,如果不同,则需要释放原来的内存空间,为被赋值对象重新分配内存空间,并将参数对象的成员变量复制到被赋值对象中。例如:
class String {
public:
String() : data(nullptr), len(0) {}
String(const char* str) {
len = strlen(str);
if (len > 0) {
data = new char[len + 1];
strncpy(data, str, len + 1);
} else {
data = nullptr;
len = 0;
}
}
String(const String& other) {
len = other.len;
if (len > 0) {
data = new char[len + 1];
strncpy(data, other.data, len + 1);
} else {
data = nullptr;
len = 0;
}
}
String& operator=(const String& other) {
if (this != &other) {
if (len > 0) delete[] data;
len = other.len;
if (len > 0) {
data = new char[len + 1];
strncpy(data, other.data, len + 1);
} else {
data = nullptr;
len = 0;
}
}
return *this;
}
~String() {delete[] data;}
//其他代码和成员变量
private:
char* data;
size_t len;
};
在这个例子中,我们定义了一个String类,它有两个拷贝构造函数和一个拷贝赋值操作符。在拷贝赋值操作符中,我们首先检查被赋值对象和参数对象是否相同,如果相同则不进行操作,如果不同则释放原来的内存空间,为被赋值对象重新分配内存空间,并将参数对象的成员变量复制到被赋值对象中。
移动构造函数和操作符
移动构造函数和操作符是C++11引入的两个新的成员函数,它们是为了提高程序的效率而生的。当我们使用一个右值对象初始化一个新的对象或者使用一个右值对象赋值给另一个对象时,移动构造函数和移动操作符就会被调用。例如:
ClassName obj1; //调用默认构造函数
ClassName obj2 = std::move(obj1); //调用移动构造函数
ClassName obj3;
obj3 = std::move(obj1); //调用移动赋值操作符
移动构造函数和操作符的定义方法如下:
class ClassName {
public:
ClassName(ClassName&& other); //移动构造函数
ClassName& operator=(ClassName&& other); //移动赋值操作符
//其他成员函数和变量
};
在移动构造函数和操作符中,我们需要将右值对象的内存地址转移给新的对象,然后将右值的成员变量赋值为默认值或者空指针。这样可以避免不必要的内存复制和赋值操作,提高程序的效率。例如:
class String {
public:
String() : data(nullptr), len(0) {}
String(const char* str) {
len = strlen(str);
if (len > 0) {
data = new char[len + 1];
strncpy(data, str, len + 1);
} else {
data = nullptr;
len = 0;
}
}
String(const String& other) {
len = other.len;
if (len > 0) {
data = new char[len + 1];
strncpy(data, other.data, len + 1);
} else {
data = nullptr;
len = 0;
}
}
String(String&& other) noexcept {
data = other.data;
len = other.len;
other.data = nullptr;
other.len = 0;
}
String& operator=(String&& other) noexcept {
if (this != &other) {
if (data) delete[] data;
data = other.data;
len = other.len;
other.data = nullptr;
other.len = 0;
}
return *this;
}
~String() {delete[] data;}
//其他代码和成员变量
private:
char* data;
size_t len;
};
在这个例子中,我们定义了一个String类,它有两个拷贝构造函数和两个移动构造函数和操作符。在移动构造函数和操作符中,我们首先将右值对象的内存地址转移给新的对象,并将右值的成员变量赋值为默认值或者空指针。这样可以避免不必要的内存复制和赋值操作,提高程序的效率。
以上就是C++中的六大默认成员函数的详解。掌握它们的作用和实现机制,可以帮助我们编写高效、健壮的程序。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++类中的六大默认成员函数详解 - Python技术站