首页 > 其他分享 >类和对象

类和对象

时间:2024-07-04 13:53:15浏览次数:3  
标签:函数 对象 res int ._ Date day

类和对象

预处理: 头文件展开、宏替换、条件编译、去除注释 -> .i
编译:检查语法、生成汇编代码 ->.s
汇编:将汇编指令转换成CPU能理解的二进制机器码 -> .o
链接:找函数实体、生成可执行文件 -> Windows: .exe Linux: .out

问:为什么C++ 支持重载,C语言不支持?

  • C语言在链接之后生成的目标文件中函数名的存储方式是直接使用函数名(因此编译也不通过),因此出现明明冲突。

  • 而C++使用函数名修饰规则,Linux下函数名的存储格式为:_Z+函数名长度+函数名+参数类型首字母因此只要符合重载规则的函数名都可重复调用。

全缺省:使用时要从左往右依次传值。

void Func(int a = 10, int b = 20);

半缺省:声明时要从左往右缺省,而且连续。

void Func2(int a, int b=20, int c = 40);

拷贝构造

文章链接
C++自动提供了以下默认成员函数(在没有显式定义的情况下):

  • 默认构造函;
  • 默认析构函数;
  • 拷贝构造函数(浅拷贝);
  • 赋值运算符(operator=);
  • 地址运算符;

当出现类的等号赋值时,会调用拷贝函数,在未定义显示拷贝构造函数的情况下,系统会调用默认的拷贝函数——即浅拷贝,它能够完成成员的复制。当数据成员中没有指针时,浅拷贝是可行的。但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而导致指针悬挂现象。所以,这时,必须采用深拷贝。
深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存数据,从而也就解决了指针悬挂的问题。简而言之,当数据成员中有指针时,必须要用深拷贝

浅拷贝:

/i/ll/?i=20200328225229505.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNTE5ODg2,size_16,color_FFFFFF,t_70

#include<iostream>
#include<assert.h>
using namespace std;
class Rect
{
public:
  Rect()
  {
   p=new int(100);
  }
 
  ~Rect()
  {
   assert(p!=NULL);
      delete p;
  }
private:
  int width;
  int height;
  int *p;
};
int main()
{
  Rect rect1;
  Rect rect2(rect1);
  return 0;
}

深拷贝:

/i/ll/?i=20200328225412648.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNTE5ODg2,size_16,color_FFFFFF,t_70

#include<iostream>
#include<assert.h>
using namespace std;
class Rect
{
public:
  Rect()
  {
   p=new int(100);
  }
  
  Rect(const Rect& r)
  {
   width=r.width;
      height=r.height;
   p=new int(100);
      *p=*(r.p);
  }
   
  ~Rect()
  {
   assert(p!=NULL);
      delete p;
  }
private:
  int width;
  int height;
  int *p;
};
int main()
{
  Rect rect1;
  Rect rect2(rect1);
  return 0;
}

Date方法类中运算符重载:

	// 运算符重载(内部实现)
	bool operator==(const Date& d) const{	// => bool operator==(Data* this, const Date& d)
		return _year == d._year			// this->_year == d._year
			&& _month == d._month
			&& _day == d._day;
	}
	bool operator>(const Date& d) const{
	// 复用实现
		return !(*this <= d);
	}
	bool operator<(const Date& d) const{
		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;
		return false;
	}
	// d1 <= d2 -> d1.operator<=(&d1, d2)
	bool operator<=(const Date& d) const{	// bool operator<=(Date *this, const Date& d)
		return *this < d || *this == d;	// 复用已实现的方法
	}
	bool operator>=(const Date& d) const{
		return !(*this < d);
	}
	bool operator!=(const Date& d) const{
		return !(*this == d);
	}
	Date operator+(int day) const{
		/*Date res(*this);
		res._day += day;
		while (res._day > GetMonthDay(res._year, res._month)) {
			res._day -= GetMonthDay(res._year, res._month);
			res._month++;
			if (res._month == 13) {
				res._year++;
				res._month = 1;
			}
		}
		return res;*/
	// 复用实现
		Date res(*this);
		res._day += day;
		return res;
	}
	//* Date& 加&可以避免(*this)不必要的拷贝构造
	/*
		*判断是否要用引用返回值:出了函数对象还在就可用引用返回
	*/
	Date& operator+=(int day) {
		if (day < 0) {
			return *this -= -day;
		}
		_day += day;
		while (_day > GetMonthDay(_year, _month)) {
			_day -= GetMonthDay(_year, _month);
			_month++;
			if (_month == 13) {
				_year++;
				_month = 1;
			}
		}
		return *this;
	}
	Date operator-(int day) const{
		//Date res(*this);
		//res._day -= day;
		//while (res._day <= 0) {
		//	//printf("%d ", res._day);
		//	res._month--;
		//	if (res._month == 0) {
		//		res._year--;
		//		res._month = 12;
		//	}
		//	// month-- 后操作的day 才是需要修改的month的day
		//	res._day += GetMonthDay(res._year, res._month);
		//}
		//return res;

		Date res(*this);
		res._day -= day;
		return res;
	}
	Date& operator-=(int day) {
		if (day < 0) {
			return *this += -day;
		}
		_day -= day;
		while (_day <= 0) {
			//printf("%d ", _day);
			_month--;
			if (_month == 0) {
				_year--;
				_month = 12;
			}
			_day += GetMonthDay(_year, _month);
		}
		return *this;
	}
	// d2 = d1 =》 d3.operator=(d1)
	Date& operator=(const Date& d) {
		if (this != &d)		// 针对避免自己给自己复制的情况
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;		// this是d3的地址,*this就是d3
	}
	// ++d1
	Date& operator++() {
		*this += 1;
		return *this;
	}
	// d1++	=》d1.operator++(&d1, 0)
	Date operator++(int) {
		Date tmp(*this);
		*this += 1;
		return tmp;
	}
	// d1 - d2
	int operator-(const Date& d) const{
		int flag = 1;
		Date max = *this;	// 拷贝构造 等价于(下同):Date max(*this);
		Date min = d;
		if (*this < d) {
			max = d;
			min = *this;
			flag = -1;		// 当小减大时
		}
		int n = 0;
		while (min != max) {
			++min;			// 尽量避免后加,会多一次拷贝构造
			++n;
		}
		return n*flag;
	}

一个能够体现调用拷贝构造的小问题

// 问程序在运行中产生几个对象
int n = 0;
class C {
public:
	C() {
		++n;
	}
	C(const C& c) {
		++n;
	}
private:
    int n;
};

//C f1(C b) {		// 6
//C& f1(C b) {		// 4
//C f1(C& b) {		// 4
C& f1(C& b) {		// 2		课件引用类型可以提高效率
	return b;
}

声明时给缺省值

private:
    int _year = 0;
    int _month = 1; 
    int _day = 1;
    // static 不可以在声明时给缺省值

explicit关键字

Date d1(1);		//构造
// 如果想避免这类隐式类型转化可以在函数前加上“explicit”=》explicit Date(int year)
Date d2 = 2;	//隐式类型转换 构造出tmp(2) + 在用tmp拷贝构造d2(tmp)+优化成直接构造
//const Date& d2 = 2;
Date d3 = d1;	//拷贝构造 

// C++11 新特性
Date d4(1, 2, 3);
// 同上通过在函数前加上“explicit”避免下面饮食转化
Date d5 = { 1, 2, 3 };

特性

  1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区。

  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明。

  3. 类静态成员可用类名::静态成员或者对象.静态成员来访问。

  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员。

  5. 静态成员也是类的成员,受public、protected、private访问限定符的限制。

问题:

  1. 静态成员函数可以调用非静态成员函数吗?不可以

  2. 非静态成员函数可以调用类的静态成员函数吗?可以

友元

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以 友元不宜多用。

友元分为:友元函数友元类

class Date1 {
public:
	friend void operator<<(ostream& out, const Date1& d);
private:
	// 声明时给缺省值
	int _year = 0;
	int _month = 1;
	int _day = 1;
};
void operator<<(ostream& out, const Date1& d) {
	out << d._year << '/' << d._month << '/' << d._day << endl;
}
	Date1 d1;
	//d1 << cout;		// d1.operator<<(cout);	迷惑逻辑
	cout << d1;			// 正确写法

小结

  1. 什么是类?

类是一种将抽象转换为用户定义类型的C++工具,它将数据表示和操纵数据的方法组合成一个整洁的包。类提供了一种封装数据和操作这些数据的方法的机制,从而使得代码更模块化、可重用和易于维护。

  1. 类如何实现抽象、封装和数据隐藏?

    抽象:通过类和对象来建模现实世界的实体,隐藏复杂的实现细节,暴露出简单的接口。

    封装:将数据和操作数据的函数捆绑在一起,保护数据不被外界随意修改。

    数据隐藏:通过访问控制(如public,private,protected)来限制对类内部数据的访问。

  2. 对象和类之间的关系是什么?

    类是对象的蓝图或模版。对象是类的实例化结果。类定义了对象的属性和行为,而对象是类的具体实现。

  3. 除了是函数之外,类函数成员与类数据成员之间的区别是什么?

    类数据成员(成员变量):存储对象的属性或状态。

    类函数成员(成员函数):定义了对象的行为或操作数据的方法。

  4. 定义一个类来表示银行账户。数据成员包括储户姓名、账号(使用字符串)和存款。成员函数执行如下操作:

    • 创建一个对象并将其初始化;
    • 显示储户姓名、账号和存款;
    • 存入参数指定的存款;
    • 取出参数指定的款项;
    class bank {
    public:
    	bank(const string& name, const string& account, double deposit) 
    	:_name(name), _account(account), _deposit(deposit)
    	{ }
    	void Show() {
    		cout << "name: " << _name << endl;
    		cout << "account: " << _account << endl;
    		cout << "deposit: " << _deposit << endl;
    	}
    	void Save(int deposit) {
    		_deposit += deposit;
    	}
    	void Withdraw(int deposit) {
    		if (deposit <= _deposit)
    			_deposit -= deposit;
    		else
    			cout << "你没有这么多钱~" << endl;
    	}
    private:
    	string _name;
    	string _account;
    	double _deposit;
    };
    
  5. 类构造函数在何时被调用?类析构函数呢?及其顺序

    构造函数:在对象创建时调用,用于初始化对象。全局先定义,然后依次线性调用。

    析构函数:在对象销毁时调用,用于清理资源。后定义的对象先析构(栈);局部对象先析构,全局对象静态对象再析构。

    image-20240704092552900

    注:拷贝构造的优化问题(了解):

    image-20240704094126731

  6. 什么是默认构造函数,拥有默认构造函数有何好处?

    默认构造函数:没有参数的构造函数。如果没有定义任何构造函数,编译器会自动生成一个默认构造函数。

    好处:1. 允许创建没有参数的对象。

    ​ 2. 方便数组和容器的初始化。

  7. **this 和 *this 是什么? **

this:指向调用成员函数的对象的指针。

*this:解引用 this 指针,返回调用成员函数对象本身。

标签:函数,对象,res,int,._,Date,day
From: https://www.cnblogs.com/chu0522/p/18283723

相关文章

  • js 深度对象筛选器
    要实现JavaScript深度对象筛选器,可以使用filter()方法结合自定义的过滤函数来处理对象数组。以下是一个示例,假设有一个包含用户信息的对象数组,需要筛选出满足特定条件的用户:constusers=[{id:1,name:'Alice',age:25,hobbies:['reading','usic']},{i......
  • 关于自定义unordered_set\unordered_map中Hash和KeyEqual:函数对象和lambda表达式简单
    以unordered_set为例,首先在cppreference中查看其模板定义:可以看到Hash类默认是std::hash<Key,KeyEqual类似,本文将Hash以函数对象写出,将KeyEqual以lambda写出。classhashvec{ public: size_toperator()(constvector<int>&vec)const{ returnhash<int>()(vec[0])+hash......
  • Java循环创建对象内存溢出怎么解决
    在Java中,如果在循环中不当地创建大量对象而不及时释放内存,很容易导致内存溢出(OutOfMemoryError)。这通常发生在以下几种情况中:(1)循环内不断创建对象但对象引用未被释放:对象被创建后,如果它们一直被引用(即使是间接的),垃圾收集器(GC)就无法回收它们占用的内存。(2)循环次数过多或对象体积......
  • Java循环创建对象内存溢出怎么解决
    在Java中,如果在循环中不当地创建大量对象而不及时释放内存,很容易导致内存溢出(OutOfMemoryError)。这通常发生在以下几种情况中:(1)循环内不断创建对象但对象引用未被释放:对象被创建后,如果它们一直被引用(即使是间接的),垃圾收集器(GC)就无法回收它们占用的内存。(2)循环次数过多或对象体......
  • 掌握Eloquent ORM:Laravel中的对象关系映射艺术
    掌握EloquentORM:Laravel中的对象关系映射艺术在现代Web应用开发中,数据库的操作是核心功能之一。Laravel框架提供了一个强大而优雅的ORM(对象关系映射)工具——Eloquent。Eloquent让数据库操作变得简单直观,同时保留了SQL的强大灵活性。本文将详细介绍如何在Laravel中使用Eloq......
  • 迭代器&&生成器&&可迭代对象
    迭代器定义:1.当类中定义了__iter__和__next__两个方法。2.__iter__方法需要返回对象本身,即:self3.__next__方法,返回下一个数据,如果没有数据了(不返回数据了),则需要抛出一个StopIteration的异常。接下来,通过代码来认识它classIT(object):def__init__(self):......
  • 《企业实战分享 · 对象存储服务OSS、S3、MinIO》
    ......
  • 将一个立方体对象的值赋给另一个立方体对象
            如果对一个类定义了两个或多个对象,则这些同类的对象之间可以互相赋值,或者说,一个对象的值可以赋给另一个同类的对象。这里所指的对象的值是指对象中所有数据成员的值。        对象之间的赋值也是通过赋值运算符"="进行的。本来,赋值运算符"="只能用来......
  • 迭代器协议、可迭代对象(迭代器)、三元表达式、生成器
    今天说的这老几位可是老牛逼了,认真看,咱们挨个介绍哈。1、迭代器协议(1)有一个next()方法(2)只能往后走不能往前退2、可迭代对象可迭代对象又叫做迭代器,什么是可迭代对象呢?很简单,满足迭代器协议的对象就是可迭代对象。说白了,就是满足前面那两条:有一个next()方法,只能往后走不能往......
  • SciTech-Psychology-Management-Behavioral Approach: Hawthorne effect霍桑效应: 主
    长期行为上:立场、思想信仰、文化理念、利益分配、投入产出比、自主自由度等,多维度决定。短期行为上:霍桑效应是一方面,被关注等社会性需要的满足,对比物质待遇也/更能激励员工的积极性和创造性[1]。社会行为上:人们相互之间,能相互影响,通过“谈话”、行为、交互甚至是“刺......