c++---类和对象(六大默认成员函数)

  • 类中默认的六个成员函数
  • 构造函数
  • 析构函数
  • 拷贝构造函数
  • 赋值操作符重载
  • 取地址和const取地址操作符重载
  • const成员函数

1. 类中默认的六个成员函数
首先看看下面代码

class A{
};
int main(){
	A a;
	return 0;
}

这个代码并没有报错,也能正常的运行,那为什么我们都没有写构造函数也能对我们的类实例化。这是因为在类中含有默认的六个成员函数,包括了构造函数,析构函数,拷贝构造函数,赋值操作符重载,取地址和const去地址操作符的重载。
在这里插入图片描述
六个默认的成员函数都有自己的功能

  • 构造函数和析构函数:主要完成初始化和清理的工作
  • 拷贝赋值:拷贝构造是使用同类对象初始化创建对象,赋值重载主要是把一个对象赋值给另一个对象
  • 取地址重载:主要是普通对象和const对象取地址。

2. 构造函数
构造函数就是完成的我们的初始化的工作,在我们每次创建一个对象的时候都会调用构造函数并且在对象的声明周期值调用一次,构造函数格式。注意我们的构造函数时没有返回值的。

类名(参数列表){
	函数体;
};
  • 无参构造
    顾名思义就是在构造函数中参数列表是没有构造函数的,比如下面的类
#include <string>
class Car{
public:
	Car(){
		
	}
private:
	std::string color;
	int price;
};
int main(){
	Car car;
	return 0;
}
  • 有参构造,就是在构造函数参数列表中含有参数。
#include <string>
class Car{
public:
	Car(std::string color,int price){
		
	}
private:
	std::string _color;
	int _price;
};
int main(){
	Car car("yellow",100000);
	return 0;
}

注意:

  • 如果类中没有显示定义构造函数,c++编译器会自动生成一个无参的默认构造函数,一旦用户自己实现了构造函数便不再生成。
  • 无参的构造函数和全缺省的构造函数都称为默认的构造函数,并且默认的构造函数只能有一个,(无参构造函数,全缺省构造函数,编译器生成的构造函数都认为是默认成员函数)
  • 对自定义类型我们的构造函数中不会做任何的事情,但是对于自定义类型会调用自定义类型的构造函数,看下面的代码
#include <string>
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 <iostream>
#include <string>
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 <iostream>
#include <string>
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操作符 (参数列表)

  • 不能通过连接其他符号来创建新的操作符,比如@#¥等等
  • 重载操作符必须有一个类类型或者枚举类型的操作数
  • 用于内置类型的操作符,器含义不能改变:比如内置的整形+。
  • 作为类成员的重载函数时,器形参看起来比操作数数目少1成员函数的操作符有一个默认的形参this,限定为第一个形参
  • .* :: sizeof ?: **.**是不能运算符重载的。

实现赋值运算符重载

#include <iostream>
#include <vector>
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;
};

赋值运算符的注意点:

  • 参数类型是引用和const,因为在传递的时候是不能去修改参数的
  • 返回值的检查(在内置类型是可以连续赋值的a=b=c),不能去改变我们原有的功能
  • 检测是否自己给自己赋值
  • 返回*this
  • 一个类如果没有显示定义赋值运算符重载,编译器会生成一个,完成按照字节序进行拷贝。

6. 取地址以及const取地址操作符重载、
取地址以及const取地址操作符重载是就是取出我们this指针,就是指向我们当前对象的地址,一般来说我们不去自己生成一个取地址操作符重载,只有在特殊的情况下才会去重载我们的const,比如我们不想让别人取的我们的地址我们可以重写,如下

#include <iostream>
#include <vector>
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指针指向的内容

猜你喜欢

转载自blog.csdn.net/boke_fengwei/article/details/90440917