1. 类中默认的六个成员函数
首先看看下面代码
class A{
};
int main(){
A a;
return 0;
}
这个代码并没有报错,也能正常的运行,那为什么我们都没有写构造函数也能对我们的类实例化。这是因为在类中含有默认的六个成员函数,包括了构造函数,析构函数,拷贝构造函数,赋值操作符重载,取地址和const去地址操作符的重载。
六个默认的成员函数都有自己的功能
2. 构造函数
构造函数就是完成的我们的初始化的工作,在我们每次创建一个对象的时候都会调用构造函数并且在对象的声明周期值调用一次,构造函数格式。注意我们的构造函数时没有返回值的。
类名(参数列表){
函数体;
};
#include
class Car{
public:
Car(){
}
private:
std::string color;
int price;
};
int main(){
Car car;
return 0;
}
#include
class Car{
public:
Car(std::string color,int price){
}
private:
std::string _color;
int _price;
};
int main(){
Car car("yellow",100000);
return 0;
}
注意:
#include
class Car{
public:
Car(std::string color,int price){
}
private:
std::string _color;
int _price;
};
int main(){
Car car("yellow",100000);
return 0;
}
3. 析构函数
析构函数和我们的构造函数时对应的,他两就标志了一个对象从出生到灭亡,析构函数就是完成对象的销毁的,完成类中的资源的清理。
格式:
~类名(){
};
析构函数特点:
在析构函数中也会分为我们的自定义类型和内置类型,当我们的内置类型的时候析构函数不会做任何事情,但是对于自定义类型会调用自定义类型的析构函数,比如下面的代码
#include
#include
class Pro{
public:
Pro(){
std::cout << "Pro()" << std::endl;
}
~Pro(){
std::cout << "~Pro" << std::endl;
}
private:
std::string name;
std::string address;
};
class Car{
public:
Car(std::string color,int price){
}
private:
std::string _color;
int _price;
Pro p;
};
void test(){
Car car("yellow", 100000);
}
int main(){
test();
system("pause");
return 0;
}
4. 拷贝构造函数
拷贝构造函数也是构造函数,他与无参构造和有参构造不同的是他是用一个对象去初始化另一个对象,一般只有一个形参,就是本类类型对象的引用
格式:
类名(const 类& 对象)
{
}
这里一定要加上我们的引用对象,当我们不加引用对象的时候在vs下就直接报错了,
原理是我们调用拷贝构造的时候不加上&的时候,我们是调用函数,形参数实参的一份拷贝,此时在拷贝形参的时候又会出现拷贝构造,这样就一直循环,程序崩溃,所以在vs上就直接报错了。引发无穷递归。
默认的拷贝构造函数时字节序值拷贝(也称为浅拷贝)
在我们没有声明拷贝构造函数的时候编译器会自动生成一个默认的拷贝构造函数,但是此时的拷贝构造函数并不适用所以的情况,当我们这里出现了在堆上申请空间的时候,比如下面的代码,看看有没有什么问题
#include
#include
class Pro{
public:
Pro(){
std::cout << "Pro()" << std::endl;
name = (char*)malloc(1024);
}
~Pro(){
std::cout << "~Pro" << std::endl;
free(name);
}
private:
char* name;
std::string address;
};
上面的代码在看起来应该是没有毛病的,但是在我们进行拷贝构造的时候呢,只是单纯的值拷贝,
此时的时候应该是出错的,因为我们在申请a之后又拷贝构造将a的值拷贝给b,所以在a和b中的char* name是指向的同一块内存,当我们调用析构函数的是会出现这块内存被释放了两次。这时候应该使用深拷贝,后面我们再对深拷贝进行详细的讲解。
5. 赋值运算符重载
赋值运算符重载就是为了代码的可读性,比如我们两个类进行赋值的时候如果不写赋值运算符重载的话就要用一个函数来实现我们的赋值,函数在调用的时候可读性没有我们的“=”高,所以c++实现了运算符的重载,在我们不实现的时候就默认生成了。运算符重载在我们的日期类的实现的时候都了解过了,这里我们再总结一下
函数原型:返回值类型 operator操作符 (参数列表)
实现赋值运算符重载
#include
#include
class car{
car& operator=(const car& c){
if (&c == this){
return *this;
}
this->name = c.name;
this->price = c.price;
return *this;
}
private:
std::string name;
int price;
};
赋值运算符的注意点:
6. 取地址以及const取地址操作符重载、
取地址以及const取地址操作符重载是就是取出我们this指针,就是指向我们当前对象的地址,一般来说我们不去自己生成一个取地址操作符重载,只有在特殊的情况下才会去重载我们的const,比如我们不想让别人取的我们的地址我们可以重写,如下
#include
#include
class car{
public:
car& operator=(car c){
if (&c == this){
return *this;
}
this->name = c.name;
this->price = c.price;
return *this;
}
car* operator&(){
return nullptr;
}
private:
std::string name;
int price;
};
int main(){
car a;
std::cout << &a << std::endl;
system("pause");
return 0;
}
就永远取地址都得到的是NULL;
7. const修饰成员函数
将const修饰的类成员函数称之为const成员函数,实际上修饰的是this指针,表明该成员函数中不能对类的任何成员进行修改
此时发现我们队this指向name进行修改的时候直接报错,所以const修饰的成员函数不能去修改this指针指向的内容