0
点赞
收藏
分享

微信扫一扫

c++:类和对象(上)

天涯学馆 2022-01-17 阅读 88
c++后端

文章目录

面向对象

🥑c语言是面向过程的,关注的是过程
🥑c++是基于面向对象的,关注的是对象

🎨面向对象三大特性:封装,继承,多态

封装:🎈1.数据和方法在类里面都放在了一起
🎈 2.访问限定符:public(公有) protected(保护) private(私有)
🔰公有可以在类外面直接访问;保护和私有不可以直接访问

🎈封装是一种更好的严格管理,不封装是一种自由管理

类和对象

1.struct

在c语言中学会了如何创建和使用一个结构体

struct student
{
	//成员变量
	char _name[20];
	int _age;
};

但是c++中,升级到类,student是类名,也是类型

struct 的默认访问限定符是公有public

//c++类跟结构体不同的是除了可以定义变量,还可以定义方法和函数
struct student
{
	//成员变量
	char _name[20];
	int _age;

	//成员方法
	void Init(const char* name, int age)
	{
		strcpy(_name, name);
		_age = age;
	}

	void Print()
	{
		cout << _name << endl;
		cout << _age << endl;
	}


};

int main()
{
	struct student s1;//兼容c,这里struct student才是类型
	student s2;      //升级到类,student是类名,也是类型

	s1.Init("张三", 13);
	s1.Print();
	return 0;
}

2.class

把struct换成class,暂时class类就定义出来了

🦇class的默认访问限定符是私有
🦇因此我们可以加访问限定符,限定范围的是该限定符的位置到下一个限定符,如果后面没有限定符,默认到类的结束

class student
{
private:
	//成员变量
	char _name[20];
	int _age;

public://限定范围的是该限定符的位置到下一个限定符
	//成员方法
	void Init(const char* name, int age)
	{
		strcpy(_name, name);
		_age = age;
	}
//private:
	void Print()
	{
		cout << _name << endl;
		cout << _age << endl;
	}
};

类对象的大小

🐞对象中存了成员变量,没存成员函数
因为每个对象都有独立的成员变量
不同对象调用成员函数,调的是同一个

🐞只保存成员变量,成员函数放在公共的代码段
在这里插入图片描述
在这里插入图片描述
结论:
计算类或者类对象大小,只看成员变量,考虑内存对齐,c++内存对齐规则跟c语言结构体一致
空类会给一个byte,这1byte不存储有效数据,只是为了和其他类区分

this指针

首先创建一个日期类

class Date
{
private:
	int _year;
	int _month;
	int _day;

public:
	void Init(int year,int month,int day)
	{
		_year = year;
		//即使成员变量跟形参同名,根据就近原则,还是会优先使用形参,但结果不是我们传的2022
		//想同名,也可以使用Date :: year,指定作用域
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year<<"-" << _month << "-" << _day << endl;
	}
};

int main()
{
	Date s1;
	s1.Init(2022, 12, 10);
	s1.Print();

	Date s2;
	s2.Init(1998, 2, 12);
	s2.Print();

	return 0;
}

🔰注意:在c++中,s2.Print();表面上没有传任何参数,实际上它隐藏一个参数this 指针
在这个类里,this指针的类型是Date*

在这里插入图片描述
💙 “//”意思是都是被编译器处理过后的

    //void Init(Date* this,int year,int month,int day)
	//void Print(Data* this)
	void Print()
	{
		cout << _year<<"-" << _month << "-" << _day << endl;
		//只有在成员变量之前加this->
		//cout << this->_year << "-" <<this-> _month << "-" <<this-> _day << endl;
	}
	
d1.Print();//会被编译器处理成d1.Print(&d1)
1. 调用成员函数时,不能显示传实参给this; 2.定义成员函数时,不能显示声明形参this;3.但是在成员函数内部 ,我们可以显示使用this

所以这么编写是错的void Print(Data* this)d1.Print(&d1)
而下面这段在成员函数内部使用就是对的

void Init(int year,int month,int day)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}

this指针本身不能被改变(Date* const this)
const 在Date*之前表示指向的内容不能被改变 在Date* 之后表示指针本身不能被改变

this存在哪?一般情况下是在栈(形参),有些编译器会放在寄存器中。

1.重要例题

// 1.下面程序能编译通过吗?
// 2.下面程序会崩溃吗?在哪里崩溃
class A
{
public:
void Show()//虽然传过来的是空指针,但并没有进行解引用操作
{
  cout<<"Show()"<<endl;//这里没有错误,正常运行
}
void PrintA()
{
  cout<<_a<<endl;//这里会运行崩溃
  //这里编译器会这么处理  cout<<this->_a<<endl;
  //this又是空指针,解引用了就会报错
}
private:
  int _a;
};

int main()
{
  A* p = nullptr;
  p->Show();//这里没有错误,正常运行
  p->PrintA();//这里就会运行崩溃
}

分析:
1.p虽然是空指针,但是p调用成员函数不会编译报错,因为空指针不是语法错误,编译器检查不出来

2.p虽然是空指针,但是p调用成员函数不会出现空指针访问。因为成员函数没有存在对象里

3.这里会把p作为实参传递给隐藏的this指针,而指针不解引用是不会报错的

类的默认成员函数

在这里插入图片描述

1.构造函数

构造函数是特殊的成员函数,需要注意的是构造函数的主要任务并不是开空间创建对象,而是初始化对象。

其特征如下:

  1. 函数名与类名相同。
  2. 无返回值。
  3. 对象实例化时编译器自动调用对应的构造函数。
  4. 构造函数可以重载
public:
	//Date()//无参
	//构成函数的重载,但是它要是无参的调用,就会有二义性
	//{
	//	 _year=0;
	//	 _month=1;
	//	 _day=1;
	//}

	Date(int year = 2000, int month = 1, int day = 10)//全缺省
	{
		_year = year;
		_month = month;
		_day = day;
	}

  1. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成,但是它没有做初始化的工作
    在这里插入图片描述
    c++里把类型分为两类:内置类型,自定义类型
    内置类型:int / char / double /指针 /数组 等
    自定义类型:struct / class 定义的类型

🛎编译器默认的构造函数,对于内置类型不做初始化处理
🛎对于自定义类型会去调用他的无参默认构造函数(不用参数就可以调用)初始化,如果没有默认构造函数就会报错
任何一个类的默认构造函数就是–不用参数就可以调用(1.全缺省;2.无参;3.不写编译器默认生成的)

无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个
语法上他们可以同时存在,但是如果有对象定义去调用就会报错
   //定义一个对象必须是下面两种形式
	Date d1;//无参,也可以进入全缺省的构造函数
	Date d2(2000,10,22);//当构造函数是我们自己写的缺省参数
	//不可以Date d1();

在这里插入图片描述

2.析构函数

像日期类,没有资源需要清理,所以Date不实现析构函数是可以的
像栈,队列等等动态开辟的则需要我们自己去写析构函数

如果我们不写,默认生成的析构函数和构造函数类似
对内置类型的成员变量不做处理,对自定义类型会去调用他的析构函数

析构函数是特殊的成员函数。
其特征如下:

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值。
  3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

class Stack
{
public:
	Stack(int capacity=4)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			cout << "malloc fail" << endl;
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	~Stack()
	{
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	int _capacity;
	int _top;
	int* _a;
};

int main()
{
   Stack s1;
   return 0;
}

3.拷贝构造函数

传值传参形参是实参的一份拷贝,这个拷贝就会调用拷贝构造,就会套娃,而Date& 是别名
c语言就没有拷贝构造

在这里插入图片描述

Date(const Date& d)//因为参数不用被修改,也能防止d2改变d1
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

如果我们没有定义拷贝构造函数,系统会生成默认的拷贝构造函数
1.内置类型成员,会完成按字节序的值 拷贝(浅拷贝)

比如:d1照样拷贝给d2

    Date d1(2000, 10, 11);
	Date d2(d1);
	

对于栈这种类就不能用默认的拷贝构造,需要自己去完成
在这里插入图片描述

2.自定义类型成员,会调用他的拷贝构造

4.赋值运算符重载

//函数名 operator操作符
//返回类型,看操作符运算后的返回值是什么
bool operator>(const Date& d1, const Date& d2)
//const Date& 的好处:既可以接收const Date&类型的对象,也可以接收非const类型的对象
//因为Date&类型的对象传给const Date& ,是权限的缩小

我们可以把它放在类里面,这样_year,_month,_day都能被访问了
👼因为在类当中,成员函数有隐藏一个参数this
👼所以我们要改成bool operator>( const Date& d2) //会被编译器处理成bool operator>(Date* const this,const Date& d)

所以最终在类里就是如下代码

class Date
{
public:
	//构造函数
	Date(int year = 1000, int month = 1, int day = 10)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	bool operator>(const Date& d)//会被编译器处理成bool operator>(Date* const this,const Date& d)
	{
		if (_year > d._year)
		{
			return true;
		}
		else if (_year == d._year && _month > d._month)
		{
			return true;
		}
		else if (_year == d._year && _month == d._month && _day > d._day)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

private:
	int _year;
	int _month;
	int _day;

};


举报

相关推荐

0 条评论