类与对象(一)

1、面向过程与面向对象

C 语言是 面向过程 的, 关注 的是 过程 ,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++ 基于面向对象 的, 关注 的是 对象 ,将一件事情拆分成不同的对象,靠对象之间的交互完
成。
例如:对于外卖平台点餐。
面向过程面向对象
点餐下单用户
商家接单商家
分配骑手骑手
骑手送餐

2、类

C++中的类(也可称为结构体)和C语言中的结构体类似,继承了结构体内存对齐,便于访问的特性,但是在C语言中结构体只能定义变量,而类中可以定义函数,常用class表示类的关键字。

/*******一般将类的声明放在.h文件中,类中函数定义放在.cpp文件,
********因为成员函数直接在类中定义,编译器会默认当作内联函数*/
class Stack {
//访问限定符的作用域:从当前访问限定符开始直到遇见下一个访问限定符或者'}'
public://公共代码区间(struct的默认访问权限),成员在类外可以直接被访问
	void Init(int capacity);//类中声明的函数称为类的方法或成员函数
	void Destroy(); //(.cpp中定义时void Stack:: Destroy())

private://私有区间(class的默认访问权限),只有类的内部成员才能访问
	int* _arr; //类中声明的函数称为类的属性或成员变量
	int _capacity;//类中声明的变量一般前面加上"_"表示区分
	int _top;//类中也可以直接给变量赋缺省值
};

2.1 类对象的存储

一般将成员函数放在公共代码区间段,相当于全局函数的声明(存在静态变量区间),在用sizeof计算类的空间大小时并不会计算进去,因此类的大小计算的是类中成员变量所占字节的个数。而成员变量则根据定义方式来决定其存储区间。当一个类为空类或者只有成员函数时,则此类只会占用一个字节来表示对象的存在,并不会在类中存储数据。

2.2 this指针

this*是一个隐藏在类成员函数中以形参的形式存在,当对象调用成员函数时,将对象地址作为实参传递给this形参,并且一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。

class Date {
public:
	//this隐藏在函数形参中,不能在函数中显示,并且this指针是常量形式不能赋值
	void Init(int year, int month, int day) {
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}
	void Print() {
		cout<< _year<<"/"<< _month <<"/"<<_day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

3、构造函数

构造函数的名字与类名相同(不需要void或者其他修饰词),创建类类型对象时由编译器自动调用,且在对象整个生命周期内只是用一次,用来初始化对象。

class Date {
public:
    Date(){//不带参数形式,两个Date构成函数重载
   _year = 1;
    _month = 1;
    _day = 1;
    }
	Date(int year,int month=7,int day=30){//带缺省参数
		_year = year;
		_month = month;
		_day = day;
	}
	void Print() {
		cout<< _year<<"/"<< _month <<"/"<<_day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main() {
	Date d1(2024);
    Date d2;    //不能写成Date d2();编译器会认为这是函数声明,导致报错
	d1.Print();
    d2.Print();
 	return 0;
}

另外,当程序员没有写构造函数时,编译器会生成默认构造函数(不带参数形式),默认构造不会初始化内置类型变量(int\char,int*....,这一类型的成员变量可以在类中声明时可以给默认值,作用和缺省参数类似),只会对自定义类型成员起作用。因此一般情况需要程序员自己写构造函数,除非成员变量全是自定义类型

4、析构函数(定义形式:~+类名)

析构函数是一种无参数无返回值和类同名的特殊函数,其作用并不是完成对对象本身的销毁,对象销毁是编译器完成的,而对象在进行销毁时会自动调用析构函数,完成对象中资源的清理工作。

class Date {
public:
	~Date() {
		cout << "~Date()" << endl;//通过打印窗口观察这段代码执行否
	}
};
int main() {
	Date d1;
	return 0;
}

另外,和构造函数一样,但是析构函数不能重载。当程序员没有写构造函数时,编译器会生成默认析构函数,如果类中没有资源申请(malloc,realloc申请的空间)时可以直接使用编译器生成的默认析构函数;有资源申请时,一定要写析构函数,否则会造成内存泄漏,因为默认析构函数不会主动释放堆中申请的空间。

5、拷贝构造函数

拷贝函数的作用是将创建的对象进行拷贝,拷贝构造函数的参数只有一个必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。

class Date {
public:
	Date() {
		_year = 1;
		_month = 1;
		_day = 1;
	}
	//如果函数写成Date(Date d),这种传值构造拷贝称为浅拷贝,调用Date d2(d1);时
    //目的是将d1中日期拷贝到d2中,调用函数后先要将d1传给形参d,又涉及到对象的拷贝,
    //因此又会调用拷贝构造函数,依次循环导致,程序无限递归。
	Date(Date& d) {
		cout << "Date(Date& d)" << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main() {
	Date d1;
	Date d2(d1);//或者写成Date d2 = d1;
	return 0;
}

另外,若没有定义拷贝构造函数,编译器会生成默认拷贝构造函数,默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。即对于上面所写的Date传值代码中即使没有定义拷贝构造,编译器也会默认生成,对象中的内置类型就按照字节方式直接拷贝。但是对于一些类中涉及资源申请时,比如栈,如果直接用编译器生成的拷贝构造函数,而默认构造函数是按照值拷贝的,当程序退出要销毁对象时,就会对同一空间调用两次析构函数,也就是释放两次空间,导致程序报错。因此这种情况要程序员自己定义拷贝构造函数。

class Stack {
public:
	Stack(size_t capacity = 4) {
		_arr = (int*)malloc(sizeof(int)* capacity);
		if (_arr == nullptr) {
			perror("malloc fail");
			exit(1);
		}
		_top = 0;
		_capacity = capacity;
	}
	Stack(const Stack& s) {//深拷贝
		_arr = (int*)malloc(sizeof(int) * s._capacity);
		if (_arr == nullptr) {
			perror("malloc fail");
			exit(1);
		}
		memcpy(_arr,s._arr,sizeof(int)*s._top);
	}
private:
	int* _arr; 
	int _capacity;
	int _top;
};

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值