C++2-class、struct 和 class 的区别、面向对象特点、类的大小、this、带默认值的函数(带缺省值的函数)、内联函数、内联函数和宏定义的区别

一、class — 类

1、什么是类:

将不同类型的数据以及与这些数据相关的操作封装在一起,所构成的新的数据类型,称为类;
不同类型的数据 — 属性;与这些数据相关的操作 — 函数;
使用类可以理解为:属性 + 函数;

2、访问权限 — public,protected,private;

class Clock
{//可以不安这顺序,但一般都为此顺序,也可以写好多个
public:
	void Set(){}
	void Show(){}
protected://在没学习继承之前,不用考虑
	int m_test;
private:
	int m_hour;//m_ 为设计规范,表示为成员
	int m_minute;
	int m_second;
};

class — 是我们当前定义类的一个关键字,区别与struct;
Clock — 当前的类名,一般首字母为大写(为编程规范,也可小写);
类当中有两个东西,一个为属性,一个为操作;

一般情况下,把属性设计成私有的,把函数或称为类和外界的接口设计成共有的;

在继承之前,protected 可以不考虑,因为在外界也不能访问;

在外界不能访问保护和私有;

所以当前函数就是一个包含三个属性的类,通过Set和Show函数访问修改三个属性;

使用示例

class Clock
{
public:
	void Set()
	{
		m_hour = 20;
		m_minute = 10;
		m_second = 30;
	}
	void Show()
	{
		cout<<m_hour<<":"<<m_minute<<":"<<m_second<<endl;
	}
protected://在没学习继承之前,不用考虑,因为保护的在外界也不能用
	int m_test;
private:
	int m_hour;//m_ 为设计规范,表示为成员
	int m_minute;
	int m_second;
};

void main()
{
	Clock c;//Clock - 类,是个类型;c - 对象;
	//外界不能访问保护和私有
	//c.m_teat = 20;//error,非法,报错,
	//c.m_hour = 20;

	c.Set;
	c.Show;

}

二、struct 和 class 的区别:

在C++里结构体和类是差不多的,当一个类比较简单,在外界都可使用,可以定义成struct;

只有一个区别:
struct 的默认权限是public — 共有的;
class 的默认权限是private — 私有的;

但还是用class去定义类,因为毕竟struct是结构体的意思,而不是类;

代码示例:

struct AA
{
	void print(){}
};

class BB
{
	void print(){}
};

void mian()
{
	AA a;
	BB b;

	a.print();
	b.print();
}

在这里插入图片描述
注释b.print(); 程序可以运行;

struct AA
{
	void print(){cout<<"AA::print"<<endl;}
};

class BB
{
	void print(){}
};

void mian()
{
	AA a;
	BB b;

	a.print();
	//b.print();
}

在这里插入图片描述
注释a.print(); 程序会报错;

struct AA
{
	void print(){cout<<"AA::print"<<endl;}
};

class BB
{
	void print(){}
};

void mian()
{
	AA a;
	BB b;

	//a.print();
	b.print();
}

在这里插入图片描述

三、面向对象特点:封装、继承、多态 ,抽象(有异议)

1、封装 — 属性(private) + 操作(public)

把我们当前的属性、操作放在一个集合,有的是共有的,有的是私有的,有的是保护的,即为封装;

2、继承 — 上层和下层的关系;

描述现实世界的一个关系,层次关系。从下层往上层说为继承,从上次往下层说,为派生;

3、多态 — 多种形态;

— C++内有四种多态:
1)重载多态 — 函数重载,运算符重载;
2)强制多态 — 强制类型转换 static_cast , dynamic_cast , const_cast,…
3)包含多态 — 虚函数的多态 — virtual
4)参数多态 — 模板(模型)

4、抽象

不是一个特点,是指用我们当前一系列对象,抽其共性的过程,称为抽象;

四、类的大小

1、一般情况下,类的大小是属性之和,普通函数不占类空间大小;

#include <iostream>
using namespace std;

class AA
{
public:
	void test(){}
	//virtual void fn1(){}//占空间大小
	//virtual void fn2(){}
	//virtual void fn3(){}
private:
	int m_i;
	char m_sex;
	int m_age;
	//12字节大小
	//static int m_num;//不占空间大小
};

void main()
{
	cout<<sizeof(AA)<<endl;
}

在这里插入图片描述

2、static 属性不占类空间大小;

在这里插入图片描述

3、虚函数

virtual 函数在类中分配一个指针的大小,并且不管有多少个virtual 函数,都是增加了一个指针的大小,4字节;
因为不管增加了几个虚函数字节,只要有虚函数定义,那么在当前类里面,就会增加且只增加一个虚指针 — 指向虚表 — 虚表内放的是虚函数的入口地址,所以只要有虚函数,就会有且只需要一个表,这一个表内存放相关函数;
在这里插入图片描述
在这里插入图片描述

五、this

在非 static (静态)成员函数中,有一个隐含的指针this,this指向本类当前类对象,即是接收当前类对象的地址;
this = & 当前对象

class Clock
{
public:
	void Set(int h) //c1.Set(12) 等价与 c1.Set(&c1,12)
	{//this = &当前对象
		m_hour = h;
		m_minute = 20;
		m_second = 30;

		/*this->m_hour = h;
		this->m_minute = 20;
		this->m_second = 30;*/
	}

	void Show()
	{
		cout<<m_hour<<":"<<m_minute<<":"<<m_second<<endl;
		cout<<this->m_hour<<":"<<this->m_minute<<":"<<this->m_second<<endl;
	}
private:
	int m_hour;
	int m_minute;
	int m_second;
};

void main()
{
	Clock c1;
	c1.Set(12);
	c1.Show();
	/*c2.Set(12,20);
	c2.Show();
	c3.Show()*/;
}

运行结果:

在这里插入图片描述

class Clock
{
public:
	void Set(int h) //c1.Set(12) 等价与 c1.Set(&c1,12)
	{//this = &当前对象
		/*m_hour = h;
		m_minute = 20;
		m_second = 30;*/

		this->m_hour = h;
		this->m_minute = 20;
		this->m_second = 30;
	}

	void Show()
	{
		cout<<m_hour<<":"<<m_minute<<":"<<m_second<<endl;
		cout<<this->m_hour<<":"<<this->m_minute<<":"<<this->m_second<<endl;
	}
private:
	int m_hour;
	int m_minute;
	int m_second;
};

void main()
{
	Clock c1;
	c1.Set(12);
	c1.Show();
}

运行结果:

在这里插入图片描述

六、带默认值的函数(带缺省值的函数)

void Set(int h=10,int m=20;int s=30)

注意:

1、一般情况下,尽量参数都带默认值。如果有不带的,一定要放在前面。即是,有一个参数带默认值了,它后面的必须带默认值;
2、参数在定义的时候带默认值,如果放在类外定义此函数,则默认值不需要再定义;

代码示例:

class Clock
{
public:
	//带默认值的函数(带缺省值的函数)
	//void Set(int h=10,int m=20;int s=30)
	//{
	//	m_hour = h;
	//	m_minute = m;
	//	m_second = s;
	//}
	//等价于一下4个函数之和
	void Set()
	{
		m_hour = 10;
		m_minute = 20;
		m_second = 30;
	}
	void Set(int h)
	{
		m_hour = h;
		m_minute = 20;
		m_second = 30;
	}
	void Set(int h,int m)
	{
		m_hour = h;
		m_minute = m;
		m_second = 30;
	}
	void Set(int h,int m,int s)
	{
		m_hour = h;
		m_minute = m;
		m_second = s;
	}
	void Show()
	{
		cout<<m_hour<<":"<<m_minute<<":"<<m_second<<endl;
	}
private:
	int m_hour;
	int m_minute;
	int m_second;
};

void main()
{
	Clock c,c1,c2,c3;

	c.Set();
	c1.Set(1);
	c2.Set(1,1);
	c3.Set(1,1,1);
	c.Show();
	c1.Show();
	c2.Show();
	c3.Show();
}

运行结果:

不带默认值:

在这里插入图片描述

带默认值:

在这里插入图片描述

将Show函数放到类外定义

class Clock
{
public:
	//带默认值的函数(带缺省值的函数)
	//void Set(int h=10,int m=20,int s=30)
	//{
	//	m_hour = h;
	//	m_minute = m;
	//	m_second = s;
	//}
	//等价于一下4个函数之和
	void Set()
	{
		m_hour = 10;
		m_minute = 20;
		m_second = 30;
	}
	void Set(int h)
	{//this = &当前对象
		m_hour = h;
		m_minute = 20;
		m_second = 30;
	}
	void Set(int h,int m)
	{
		m_hour = h;
		m_minute = m;
		m_second = 30;
	}
	void Set(int h,int m,int s)
	{
		m_hour = h;
		m_minute = m;
		m_second = s;
	}
	void Show();
	//{
	//	cout<<m_hour<<":"<<m_minute<<":"<<m_second<<endl;
	//}
private:
	int m_hour;
	int m_minute;
	int m_second;
};
void Clock::Show()
{
	cout<<m_hour<<":"<<m_minute<<":"<<m_second<<endl;
}
//void ::Set(int h=10,int m=20,int s=30)//::前不加,表示全局,与类里的Set是不一样的,类比于Show,
//void Set(int h=10,int m=20,int s=30)//函数名不同,与类里的Set构成不了重载
void Set(int h,int m,int s)//参数在定义的时候带默认值,如果放在类外定义此函数,则默认值不需要再定义;
{
}
void main()
{
	Clock c,c1,c2,c3;

	c.Set();
	c1.Set(1);
	c2.Set(1,1);
	c3.Set(1,1,1);
	c.Show();
	c1.Show();
	c2.Show();
	c3.Show();
}

运行结果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

简易测试代码:

class A
{
public:
	void fn(int i = 0,int j = 0,int k = 0)
	{
		m_i = i;
		m_j = j;
		m_k = k;
	}
	void Print(){cout<<m_i<<" "<<m_j<<" "<<m_k<<endl;}
private:
	int m_i;
	int m_j;
	int m_k;
};

void main()
{
	A a,b,c,d;
	a.fn();
	b.fn(1);
	c.fn(1,2);
	d.fn(1,2,3);

	a.Print();
	b.Print();
	c.Print();
	d.Print();
}

运行结果:

在这里插入图片描述

七、内联函数

函数调用 — 有一个时间消耗和空间消耗;
内联函数 — 代码比较短小(一般不会超过5行),功能比较简单(没有比较复杂的判断和循环语句)的一些函数,放在类里面直接定义,这些函数可以作为内联函数。在函数调用时直接将函嵌入到当前调用处,直接执行,不会有函数调用的转移。
所以一般可作为内联的函数:简单的赋值,输出,返回函数。
内联函数还有:隐示内联 ,显示内联(加了个inline);

内联函数存在的目的:用空间消耗换取了时间的消耗;

注意:函数运行时会自行判断是否进行inline,代码编写时加的inline只是建议。编译器会在时间消耗和空间消耗进行选择。例如示例代码内的Sort()函数。所以,一般情况下,可以不用刻意去写inline。

class A
{
public:
	void fn(int i = 0,int j = 0,int k = 0);
	void Print(){cout<<m_i<<" "<<m_j<<" "<<m_k<<endl;}//隐示内联
	inline void Sort()//这个不会安装inline来编译
	{
		//.......
	}
private:
	int m_i;
	int m_j;
	int m_k;
};

void A::fn(int i,int j,int k)
{
	m_i = i;
	m_j = j;
	m_k = k;
}

void main()
{
	A a,b,c,d;
	a.fn();
	b.fn(1);
	c.fn(1,2);
	d.fn(1,2,3);

	a.Print();
	b.Print();
	c.Print();
	d.Print();
}

例如示例函数,当运行主函数时,函数调用的流程是,运行A a,b,c,d;,运行a.fn();,遇到一个fn(),fn这个函数名,它代表函数的入口地址,(注意,测试代码内fn函数放在类外定义,不是内联)遇见这个地址,程序就会跑到这个地址去运行,不进行内联,所以程序的流程就发生改变,从main跑到A::fn内执行,执行完后,再跑回main函数内继续执行。
而运行a.Print();时,因为Print函数在类里直接定义,并且代码比较短小,功能比较简单,编译器就将其当作内联函数进行编译,就会进行内联程序流程不发生变化,直接把Print这段代码的实现体void Print(){cout<<m_i<<" “<<m_j<<” “<<m_k<<endl;},直接嵌入到当前运行的代码。此时,a.Print();就变成了Print函数的实现体 — void Print(){cout<<m_i<<” “<<m_j<<” "<<m_k<<endl;} 这样省去了时间的消耗,
但浪费了空间
,即为用空间消耗换取了时间的消耗

笔试题:内联函数和宏定义的区别

1、内联是函数,宏是预处理命令;
2、内联的在运行时执行,宏是编译之前执行;
3、内联不会出现歧义,宏会出现歧义(会出现二义性);

使用宏定义不成文的规矩:

遇到宏定义,就原模原样的区替换;
写宏定义时,把括号加到不能加为止;

二义性宏定义举例:

示例1:出现二义性

#define N 3k //正常的宏定义,本身没有问题,但替换的时候容易出现问题
#define S(a,b) a+b //出现二义性

void main()
{
	cout<<S(3,4)/S(3,4)<<endl;//解析为3+4/3+4
	cout<<3+4/3+4<<endl;
}

在这里插入图片描述
对上一示例进行修改,解决二义性

#define N 3k //正常的宏定义,本身没有问题,但替换的时候容易出现问题
//#define S(a,b) a+b //出现二义性
#define S(a,b) (a+b) //正常

void main()
{
	cout<<S(3,4)/S(3,4)<<endl;//解析为(3+4)/(3+4)
	cout<<3+4/3+4<<endl;
}

在这里插入图片描述
示例二:
出现二义性:

#define N 3k //正常的宏定义,本身没有问题,但替换的时候容易出现问题
#define S(a,b) (a*b)  //出现二义性
//#define S(a,b) ((a)*(b)) 

void main()
{
	cout<<S(3+3,4+4)<<endl;//解析为3+3*4+4
	cout<<3+3*4+4<<endl;
}

在这里插入图片描述
对上一示例进行修改,解决二义性

#define N 3k //正常的宏定义,本身没有问题,但替换的时候容易出现问题
//#define S(a,b) (a*b)
#define S(a,b) ((a)*(b)) 

void main()
{
	cout<<S(3+3,4+4)<<endl;//解析为3+3*4+4
	cout<<(3+3)*(4+4)<<endl;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值