4、C++中的类

一、C++中的类

1、类的三大特性:

  • 封装:

    • 1、把功能函数接口封装到类中
    • 2、把数据成员封装到类中
  • 继承:

    • 1、子类继承父类的函数接口或者数据,不需要重新开发接口,提高代码复用性
  • 多态:

    • 同一操作作用于不同的对象,可以有不同的解释,从而实现不同的结果。它能够提高代码的灵活性和可维护性。

2、面向对象编程的优点

  • 1、代码容易修改与维护(因为每个对象是独立的,只需要修改对象的内容即可)
  • 2、提高代码复用性(有继承、多态、函数重载),会少些很多代码
  • 3、能更好的设计复杂的程序

二、C++中类的定义

class 类名
{
	//成员列表
}

这是最简单的定义方式,但是这样定义出来的类,是没有任何意义的。因为类中的成员都是私有成员,外界是无法访问的。

1、一般类的定义方式

class 类名
{
	public : //共有成员,外界可以访问
	
	protected : //保护成员,只有派生类可以访问
	
	private :  //私有成员,只有当前类内部可以访问
}

定义一个类的例子:

class base
{
	public:
		int a; //外界可以访问,因为是共有成员
	protected:
		int b; //子类可以访问,因为是保护成员
	private:
		int c; //只有类的内部可以访问 ,因为是私有成员
};

三、C++中类的构造函数

1、构造函数:

  • 1、函数名与类名相同
  • 2、函数没有返回值
  • 3、函数不需要调用,在创建对象时会自动调用

2、构造函数的作用:

  • 用于在创建对象时,对对象中的数据成员进行初始化。

3、构造函数的语法:

class 类名
{
	public:
	类名() //构造函数
	{
	}
}

例子:
class base
{
	public:
	base()
	{
		cout << "调用构造函数" << endl;
	}
}

注意:

  • 构造函数必须要写在公共区,因为在创建对象时会自动调用构造函数。如果不在公共区,则无法调用,会导致创建对象失败
  • 假设用户没有写任何的构造函数,则系统会自动生成一个无参的构造函数

提示:

  • 1、构造函数也支持函数重载
  • 2、构造函数也支持默认参数

练习:设计一个学生类 ,共有成员 name ,保护成员money ,私有成员 id ,设计一个构造函数去初始化这些数据成员。 并设计一个输出接口。

#include <iostream>
using namespace std;

extern "C"
{
    #include <string.h>
}

class student
{
    public:
        student(const char *Name, int Money, int ID) : Money(Money), ID(ID)
        {
            strcpy(this->Name, Name);
        }
        void show()
        {
            cout << this->Name << this->Money << this->ID << endl;
        }

        char Name[32];
    protected:
        int Money;
    private:
        int ID;
};

int main()
{
    student stu("张三", 100, 180);
    stu.show();
}

四、this 指针

作用:

  • 用来区分类内成员与内外成员
  • 每当用户创建一个对象时,这个类就会自动生成一个this指针,指向当前类的首地址
base(int a,int b,int c)
{
	//this->a 当前类-》a 成员
	this->a=a;
	this->b=b;
	this->c=c;
}
//不使用 this 指针那么 a ,b ,c 变量的名字要重新修改!!

五、malloc 与 new 的区别

#include <iostream>
using namespace std;

extern "C"
{
    #include <stdlib.h>
}

class base 
{
	public:
		base(int a)
		{
			cout << "调用based的构造函数" <<endl;
			this->a = a;
		}
		
		void show()
		{
			cout << a << endl;
		}
		
	private:
		int a;
};

int main()
{
	//利用malloc分配base的空间
	base *p = (base *)malloc(sizeof(base));
	p->show();
	
	//利用new分配base空间
	base *q = new base(10086);
	q->show();
}

注意:

  • 使用malloc开辟类的空间,不会调用类的构造函数,会导致类中的成员无法初始化
  • 使用new开辟类的堆空间,可以调用构造函数去初始化类中的成员

六、析构函数

  • 1、函数名与类名相同,在函数名前面加 ~
  • 2、析构函数没有返回值,没有参数
  • 3、当对象销毁时,自动调用。可以用析构函数来释放类中成员的空间

作用:

  • 用于释放构造函数中初始化的数据成员

注意:

  • 析构函数是不可以重载的,因为重载的依据是函数参数, 析构函数没有参数
  • 析构函数也要编写到公共区域中,因为对象销毁时自动调用

在这里插入图片描述

七、类中的大小计算

字节对齐原则:

在这里插入图片描述
回顾结构体的大小计算:

struct node
{
	char a;
	int b;
	short c;
	double d;
} //在64位的操作系统中计算该结构体的大小 -》24

总结:

  • 结构体的空间大小是根据字节对齐原则的,在32位与64位系统中对齐原则是不一样的。 具体的对齐原则请看上述表格

类中的空间大小分配与结构体一样

//当前类中 最大的字节是 int 所以按照4字节对齐原则
class base
{
	char a; //分配4个 char使用 1个剩下 3 个
	short b; //剩下的3个可以提供2个给 short去使用 所以剩下1 个
	int c; //重新分配 4 个空间
}; //->8
class base1
{
	char a; //分配4个
	int c; //分配4个
	short b; //分配4个
}; //根据 4 自己对齐原则 -》 12

结论:

  • 在设计类的数据成员时,应该把数据类型,从小到大排序的去定义,这样定义出来的类,所占用的内存空间最小。

类中的大小与成员属性关系:

在这里插入图片描述

类中的大小与成员函数的关系:

class base2
{
	public:
		void func() //在没有调用 func 的时候,b,c的空间是不会分配
		{ 
			//b 和 c 根本都不属于 base2类的空间中
			int b;//func 里面的局部变量 ,局部变量是临时存在的
			int c;
		}
		int a;
}; //4

结论:

  • 类中的空间大小,只于类中的数据成员有关,与类中的成员函数无关。(除了虚函数

提示: 空类的大小为1

八、构造函数的参数列表初始化

作用:

  • 在构造函数中的一种特殊初始化方式,用于初始化类中的数据成员
    在这里插入图片描述

提示:

  • 这种初始化方式并不是万能的,如果数据类型不支持 = 赋值,就不可用该方式。列如:数组、字符串

九、拷贝构造函数 (重点,难点)

系统自动生成的浅拷贝方法::

#include <iostream>
using namespace std;

class base
{
	public:
		base(int a, int b, int c) : a(a), b(b), c(c){}
		void show()
		{
			cout << a << endl;
			cout << b << endl;
			cout << c << endl;
		}
	private:
		int a, b, c;
};

int main()
{
	base a(10, 20, 30);
	a.show();

	//通过一个对象去初始化另外一个人对象
	base b = a;
	//系统会自动生成一个浅拷贝构造函数,把a对象的数据赋值给b对象
	b.show();
}

在这里插入图片描述
重写系统的拷贝构造函数:

  • 在重写拷贝构造函数时,应当传递参数为引用,此时会重写系统默认的拷贝构造函数。通过重写的拷贝构造函数,可以实现深拷贝

语法:

class 类名
{
	public:
		类名(类名 &a) //重写后的拷贝构造函数 ,
		{
		}
}

提示:

  • 当用户重写拷贝构造函数之后,系统就不会自动生成一个浅拷贝的方法
    在这里插入图片描述
#include <iostream>
using namespace std;

class base
{
	public:
		base(int a, int b, int c) : a(a), b(b), c(c){}
		base (base &a)
		{
			cout << "调用拷贝构造函数" << endl;
			this->a = a.a;
			this->b = a.b;
			this->c = a.c;
		}

		void show()
		{
			cout << a << endl;
			cout << b << endl;
			cout << c << endl;
		}

	private:
		int a, b, c;
};

int main()
{
	base a(10, 20, 30);
	a.show();

	//通过一个对象去初始化另外一个人对象
	base b = a;
	//系统会自动生成一个浅拷贝构造函数,把a对象的数据赋值给b对象
	b.show();
}

十、深拷贝

为什么需要深拷贝??

  • 因为两个对象在共用一个地址空间。这是非常危险的。
    在这里插入图片描述

重写拷贝构造函数,实现深拷贝:

#include <iostream>
using namespace std;

extern "C"
{
	#include <string.h>
}

class base
{
	public:
		base (const char *str)
		{
			//分配p所指向的堆空间
			p = new char[1024];
			//把str数据赋值到堆空间中
			strcpy(p, str);
		}
		//重写拷贝构造函数,实现深拷贝!!!!!
		base (base &a)
		{
			cout << "调用重写后的拷贝构造函数,实现深拷贝"<< endl;
			//分配p所指向的堆空间
			p = new char[1024];
			strcpy(p, a.p);
		}

		void show()
		{
			cout << "堆空间的地址" << (void *)p <<"内容" << p << endl;
		}

		char *p;
};

int main()
{
	base a("hello");
	a.show();

	base b = a;
	b.show();
}

在这里插入图片描述
使用深拷贝之后,两个对象操作的堆空间地址就不一样了。

十一、类外定义类中的成员函数

语法:

返回值 类名::函数名(参数列表)
{

}
-----------------------------------
例子:
class base
{
	public:
		void show(); //声明
};
//类外定义show 接口
void base::show()
{
	cout << "show base" << endl;
}

练习:内外实现函数成员

#include <iostream>
using namespace std;

extern "C"
{
	#include <string.h>
}

class base
{
	public:
		base(int size,const char *str);
		base(base &a); //深拷贝函数
		~base();
		void show();
	private:
		int size;
		char *p; //指向一块堆空间
};

base :: base(int size,const char *str)
{
	cout << "base 构造函数" << endl;
	this->size = size;
	p = new char[size];
	strcpy(p, str);
}

base :: base(base &a)
{
	cout << "base 深拷贝构造函数" << endl;
	p = new char[a.size];
	strcpy(p, a.p);
}

base :: ~base()
{
	cout << "base 析构函数" << endl;
	delete []p;
}

void base :: show()
{
	cout << p << endl;
}

int main()
{
	base a(1024, "Hello");
	a.show();

	base b = a;
	b.show();
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值