C++类和对象

时间:2024-10-23 17:44:40

系列文章目录

提示:C++类和对象的知识点,后续再补充


文章目录

  • 系列文章目录
    • 1.1C和C++中的结构体struct
    • 1.2 类声明和变量分离
    • 1.3 类的访问限定符和封装
    • 1.4 类外使用成员函数 在类外使用::作用域解析符来指明成员属于哪个类
    • 1.5 声明和定义的拓展
    • 1.6 对象中只会存储成员变量,而不会存储成员函数。why?
    • 1.7 类对象中隐含的this指针
    • 1.8 构造函数:初始化对象
    • 1.9 无参的构建函数和全缺省构造函数
    • 1.10 析构函数
    • 1.11 拷贝构造 :采用同类对象来构造新的对象
    • 1.12 运算符重载
    • 1.13
    • 1.14
    • 1.15
    • 1.16


1.1C和C++中的结构体struct

C中,结构体只能定义变量
C++中,结构体可以定义变量和函数,C++中的结构体应该叫类。

struct Student
{
	void SteStudentInfo(const char* name,const char* gender,int age)
	{
		strcpy(_name,name);
		strcpy(_gender,gender);
		_age = age;
	}
	void printStudentInfo()
	{
		cout<<_name<<" "<<_gender<<" "<<_age<<endl;
	}
	char _name[20];
	char _gender[3];
	int _age;
}

但结构体它的默认为权限公开,所有对象都可以访问 结构体定义的变量,没有默认私有,就是封装特性。所有C++专门定义一个class类来实现面向对象编程

1.2 类声明和变量分离

person.h 文件中
class Person
{
	public:
		void ShowInfo();
	private:
		char* _name;
		char* _sex;
		int _age;
}
person.cpp文件中
#inclde "person.h"
void Person::showInfo()
{
	cout<<_name<<"_"<<_sex<<"_"<<_age<<_endl;
}

1.3 类的访问限定符和封装

public(公有) 类外也可以访问
protected(保护)和private(私有)类外无法访问

class默认访问权限:私有 private
struct默认访问权限:共有 public 兼容C

1.4 类外使用成员函数 在类外使用::作用域解析符来指明成员属于哪个类

class Person
{
	public:
		void ShowInfo();
	private:
		char* _name;
		char* _sex;
		int _age;
}void Person::showInfo()  //成员函数类外需要指明所属类
{
	cout<<_name<<"_"<<_sex<<"_"<<_age<<_endl;
}

1.5 声明和定义的拓展

声明是一种承诺,其不分配空间;
定义是对象的实例化,分配空间;
声明可以多次,但是定义只能一次
声明可以在多个源文件中,通过#ifndef来解决,而定义只能在一个源文件中。

1.定义的情况
万物皆可以对象
类名 对象名;(数据类型 变量) +函数定义

```cpp
int x;  //变量定义 分配空间
Person p; //对象定义  分配空间

int Add(int x,int y)  //函数定义 分配栈空间
{
	return x+y;
}
1.声明的情况
extern int x;
(extern) int Add(intx,int y);

1.6 对象中只会存储成员变量,而不会存储成员函数。why?

一个类可以实例化出多个对象,这些对象的成员函数都一样,变量值不一样,为了节约空间,会把成员变量存储在对象中,成员函数存储在公共代码区。
注意:如果一个空对象有一个字节的空间,why,占位,方便取值。

1.7 类对象中隐含的this指针

1.this指针指向 谁调用指向谁, this指针是类中的成员函数的第一个参数
2.this指针不能为空,空的this,会导致 this->变量是报错

class Date
{
publicvoid Init(int year,int month,int day)
	{
		_year = year;
		_month =month;
		_day = day; 
	}
	>>>>>>>>>>>>>>>>编译器编译为>>>>>>>>>>>>>>>>>>
	void Init(Data* this,int year,int month,int day)
	{
		this->_year = year;
		this->_month =month;
		this->_day = day; 
	}
	>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
	private:
	int _year;
	int _month;
	int _day;
};
int mian()
{
	Date d1;
	d1.Init(2020,4,7)//
	>>>>>>>>>>>>>>>>编译器编译为>>>>>>>>>>>>>>>>>>
	d1.Init(&d1,2020,4,7);
	>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
}

1.8 构造函数:初始化对象

1.函数名和类名一样
2.无返回值
3.对象实例化时自动调用
4.构造函数可以重载
5.如果未定义构造函数,编译器会自动生成一个无参的默认构造函数
6.默认的析构在定义对象 会随机初始化内置类型的值
对于自定义类型 会调用对于的构造函数

class Time
{
	public: 
	Time() //构造函数   函数名和类名一样  无返回值
	{
		_hour = 0;
		_minute = 0;
		_second =0;
	}
	private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
	void Print()
	{
		cout<<_year<<"_"<<_month<<"_"<<_day<<endl;
	}
	private:
	int _year; 
	int _month;
	int _day;
	Time _t;
};
int main()
{
	Date d1;
	//定义对象d1时,
	对象空间会创建变量int _year   int _month  int _day  这些对调用g++编译器生成的构造函数初始化为随机值
	而创建自定义类型Time _t ,也相当于实例化Time类,会自动调用Time的构造函数,使得_t._hour = 0;_t._minute = 0;_t._second =0;
	
	d1.Print();
	//_year  随机值
	//_month 
	return 0;
}

1.9 无参的构建函数和全缺省构造函数

为了兼容 Data d1;和Data d1(2020,4,6);这二种写法

可以采用函数重载来实现,但需要写二个函数
class Date
{
publicDate(int year,int month,int day)//兼容Data d1(2020,4,6);
	{
		_year = year;
		_month =month;
		_day = day; 
	}
	 Date()//兼容Data d1;
	{
		_year = 0;
		_month =0;
		_day = 0; 
	}
	>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
				采用全缺省构造函数可以合二为一
	Date(int year=0,int month=0,int da=0)//兼容Data d1(2020,4,6);和//Data d1;
	{
		_year = year;
		_month =month;
		_day = day; 
	}
	>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>		
	private:
	int _year;
	int _month;
	int _day;
};

1.10 析构函数

对象什么周期到了,自动调用,用来清理和释放空间资源。
1.函数名为 ~类名
2.无参 无返回值
3.一个类只有一个析构函数
4.未定义,编译器会自动生成一个默认析构函数
5.自定义类型 会调用它自身的构建函数/析构函数

class Stack
{
	~Stack()
	if(_a)
	{
	free(_a);
	_a = nullptr;
	_size = _capacity = 0;
	}
	private:
		int * _a;
		int _size;
		int _capacity;		
	
};

1.11 拷贝构造 :采用同类对象来构造新的对象

以下是润色后的内容:


注意:使用传值的方式来实现拷贝构造函数会导致无限递归。在创建对象时,会调用一次拷贝构造函数。

因此,应该采用传引用的方式来实现拷贝构造函数。

疑问:如果传的是引用,那 d1d2 不是同一块空间吗?

传递引用d1 作为引用传递给拷贝构造函数,这只是为了访问 d1 的成员,而不是共享内存。

创建副本:拷贝构造函数会创建一个新的对象 d2,并将 d1 的成员值复制到 d2 中。因此,d2d1 是两个不同的对象,存在于不同的内存地址。

在创建对象时,会为 d2 分配一个新的对象空间(深拷贝),而传引用不会再次拷贝。

**总结:**使用传值的方式来实现拷贝构造函数,在创建对象时存在一个调用拷贝构造函数(产生新空间d2),在传参时还有创建一个临时对象tmp,这样起码会有二次深拷贝。

而采用采用传引用的方式来实现拷贝构造函数。只会在创建对象时存在一个调用拷贝构造函数(产生新空间d2),在传参时不在创建临时对象tmp,这样起码就不会有第二次深拷贝。

class Data
{	
	Data(Date d)//存在递归拷贝的问题
	{
		_year =d._year;
		_month = d._month;
		_day = d._day;
	}
	private:
	int _year;
	int _month;
	int _day;
};

Data d2(d1);//或者Data d2=d1;二者等效

关于构造函数Data(Date d)//存在递归拷贝的问题分析:
1.对象初始化时会地调用构造函数,调用之前要传参即将d1传给Data(Date d)中的参数d,由于这个是通过传值的方式传参,会创建一个临时Date tmp=d,会再次调用构造函数,调用之前要传参即将tmp传给Data(Date d),往复调用,无限递归。

无法理解记住即可

在这里插入图片描述

1.12 运算符重载

作用:实现自定义类型采用运算符 ,例如:

class Data
{	
	Data(Date &d)//
	{
		_year =d._year;
		_month = d._month;
		_day = d._day;
	}
	private:
	int _year;
	int _month;
	int _day;
};

Data d1;
Data d2;
d2-d1;//需要自己定义
d2 == d1;//会被编译器编译为 operator==(d2,d1);
d1 > d2;

1.13

1.14

1.15

1.16