C++ 类与对象

一、面向过程与面向对象

      C语言是面向过程编程,C语言编程的过程:主函数,定义变量,调用函数然后实现。面向过程编程是一种非常具体,要面面俱到的的编程方式。而面向对象是以对象为单位来进行编程,比较像正常人的思维。

如何理解两者本质

面向过程是具体化的,流程化的。解决一个问题,需要一步一步分析需要怎样,然后需要怎样,一步一步实现的。面向对象是模型化的,抽象出一个类,这是一个封闭的环境,在这个环境中有数据有解决问题的方法,你如果需要什么功能直接使用就可以了,至于是怎么实现的,你不用知道。

从代码层面来看,面向对象和面向过程的主要区别就是数据是单独存储还是与操作存储在一起。在类的里边,实现具体的功能还是需要流程化、具体化的代码去实现的,在类里还是需要具体的算法来实现的。总结来说面向对象的底层还是面向过程,面向过程抽象成类,然后封装,方便使用就是面向对象。

面向过程与面向对象的优缺点


面向过程
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源,比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展

面向对象
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性, 可以设计出低耦合的系统,使系统更加灵活、更加易于维护
缺点:性能比面向过程低


 

二、如何理解OOP


面向对象语言的四大特征是 抽象、封装、继承、多态( 三大特征:封装和数据隐藏 继承 多态 )

抽象
数据抽象是指只向外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不呈现细节。
数据抽象是一种依赖于接口和实现分离的编程(设计)技术。
简单来说,抽象性是指程序有能力忽略正在处理中信息的某些方面,即对信息主要方面关注的能力。

封装
封装也叫做信息封装,指在设计和确定模块时,使得一个模块内包含的信息(过程或数据),对于不需要这些信息的模块来说,是不能访问的。
数据隐藏:防止程序直接访问数据被称为数据隐藏,数据隐藏不仅可以防止直接访问数据,还让开发者(类的用户)无需了解数据是如何被表示的。

继承
如果一个类A继承自另一个类B,就把这个A称为"B的子类",而把B称为"A的父类"。继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码。在令子类继承父类的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类的原有属性和方法,使其获得与父类不同的功能。另外,为子类追加新的属性和方法也是常见的做法。

多态
同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。简单的说:就是用基类的引用指向子类的对象

 

定义

类是一种复杂的数据类型,它是将不同类型的数据和与这些数据相关的操作封装在一起的集合体。这有点像C语言中的结构,唯一不同的就是结构没有定义所说的“数据相关的操作”,“数据相关的操作”就是我们平常经常看到的“方法”,因此,类具有更高的抽象性,类中的数据具有隐藏性,类还具有封装性。

  类的结构(也即类的组成)是用来确定一类对象的行为的,而这些行为是通过类的内部数据结构和相关的操作来确定的。这些行为是通过一种操作接口来描述的(也即平时我们所看到的类的成员函数),使用者只关心的是接口的功能(也就是我们只关心类的各个成员函数的功能),对它是如何实现的并不感兴趣。而操作接口又被称为这类对象向其他对象所提供的服务。

类的构成包括数据成员和成员函数。数据成员对应类的属性,类的数据成员也是一种数据类型,并不需要分配内存。成员函数则用于操作类的各项属性,是一个类具有的特有的操作,比如“学生”可以“上课”,而“水果”则不能。类和外界发生交互的操作称为接口。

class CGoods 
{
private:
	//成员变量
	char mName[20];//商品名称
	int mAmount;//数量
	float mPrince;//价格
public:
		//成员方法
	void init(char *m,int a,float p)
	{
		strcpy(mName, m);

	}
	CGoods(char *m,int a = 0,float p = 0.0)
	{
		strcpy(m, mName);
		mAmount = a;
		mPrince = p;
	}
	
	void show();
	
};
void CGoods::show()
{
	cout << mName << endl;

}

计算对象内存占用大小的时候,只计算对象的成员变量, 不包括对象的成员方法,一个类定义的很多对象,它们都有自己的一份成员变量,但是它们共享方法,在 .text段存储
 

类通过访问限定符(public private protected)来体现OOP 成员方法的定义有两个地方:类里边直接定义、在类外定义。

类只是实体空间的抽象说明    类对应ADT 抽象数据类型     类不占空间

struct和class区别

1、C结构体中不能包含函数,C++结构体仍然可以定义类,允许有内部成员函数。

2、C语言的结构体是不可以继承的,C++的结构体是可以从其他的结构体或者类继承过来的,和类一样,实现了代码的复用。

4、访问权限:C的结构体对内部成员变量的访问权限只能是public,而C++允许public,protected,private三种。

5、C定义结构体变量时需要加struct关键字,C++中定义结构体变量时可以不加struct关键字。

6、gcc下分别用C和C++定义空结构体,sizeof() 后的结果分别为0 和 1,vs下C 要求一个结构或联合至少有一个成员。一个类能够实例化,编译器就需给它分配内存空间,来指示类对象的地址。这里编译器默认分配了一个字节(如:char),以便标记可能初始化的类对象,同时使空类占用的空间也最少(即1字节)。
7、C++结构体内部成员变量及成员函数默认的访问级别是public,而c++类的内部成员变量及成员函数的默认访问级别是private。
8、C++结构体的继承默认是public,而c++类的继承默认是private。

三、类的构造函数和析构函数

默认构造函数以及带参数的构造函数

构造函数是专门做对象的初始化操作
C++ 默认构造函数是对类中的参数提供默认值的构造函数,一般情况下,是一个没有参数值的空函数,也可以提供一些的默认值的构造函数,如果用户没有定义构造函数,那么编译器会给类提供一个默认的构造函数,但是只要用户自定义了任意一个构造函数,那么编译器就不会提供默认的构造函数,这种情况下,容易编译报错,所以正确的写法就是用户在定义构造函数的时候,也需要添加一个默认的构造函数,这样就不会造成编译报错。

带参数的构造函数需要用户自己定义,编译器只是提供一个默认的构造函数。 构造函数可以有多个

数据成员的初始化顺序与它们在 类中声明的顺序 相同,与初始化列表中的排列顺序无关。

析构函数

1、函数名是在类名前加上~,无参数且无返回值
2、一个类只能有且有一个析构函数,如果没有显式的定义,系统会生成一个缺省的析构函数(合成析构函数)
3、析构函数不能重载。每有一次构造函数的调用就会有一次析构函数的调用
类的析构函数是类的一个成员函数,格式为 ~ 类名() 。它是执行与构造函数相反的操作

const int STACK_SIZE = 20;
class CSeqStack
{
public:
	// 构造函数可以重载 专门做对象成员初始化操作
	// 默认构造  CSeqStack s1;
	CSeqStack()
	{
	mTop = 0;
	mSize = 10;
	mpStack = new int[mSize];
	cout << "CSeqStack()" << endl;
	}
	// 带参数的构造函数 CSeqStack s1; CSeqStack s2(20);
	CSeqStack(int size = 10)
	{
		mTop = 0;
		mSize = size;
		mpStack = new int[mSize];
		cout << "CSeqStack(int)" << endl;
	}

	// 析构函数只能有一个 专门做资源释放操作
	~CSeqStack()
	{
		delete[]mpStack;
		cout << "~CSeqStack()" << endl;
	}
	void push(int data)  // 入栈操作
	{
		if (full())
		{
			// 2被的扩容 realloc
			int *ptmp = new int[mSize * 2];
			// memcpy(ptmp, mpStack, sizeof(int)*mSize);
			for (int i = 0; i < mSize; ++i)
			{
				ptmp[i] = mpStack[i];
			}

			delete[]mpStack;
			mpStack = ptmp;
			mSize *= 2;
		}
		mpStack[mTop++] = data;
	}
	void pop()  // 出栈操作
	{
		if (empty())
			return;
		--mTop;
	}
	int top()   // 获取栈顶元素
	{
		if (empty())
			throw "栈空了"; // 抛出异常结束函数运行就可以了
		return mpStack[mTop - 1];
	}
	bool full() // 栈满
	{
		return mTop == mSize;
	}
	bool empty() // 栈空
	{
		return mTop == 0;
	}
private:
	int *mpStack;
	int mTop;
	int mSize;
};

在一般情况下,调用析构函数的次序正好与调用构造函数的次序相反,可以简记为:先构造的后析构,后构造的先析构。

什么是this指针

编译器调用成员方法时,编译器编译成,调用方法是,把对象的地址当做实参传递进去,那么所有成员方法在编译时就会自动添加一个形参,就是this指针,在成员方法中,用来区别不同的对象,谁调用方法,this指针指向谁。

1、为什么this指针不能再静态函数中使用?

静态成员函数并不是针对某个类的实例对象,而是属于整个类的,为所有的对象实例所共有。其作用域的范围内是全局的,独立于类的对象之外的,它只对类内部的静态成员变量做操作。当实例化一个类的对象时候,里面不存在静态成员的。this指针是相当于一个类的实例的指针,this是用来操作对象实例的内容的,既然静态成员函数和变量都是独立于类的实例对象之外的,它就不能使用this指针,也不能操作静态成员。

2、this指针是什么时候创建的?

this在成员函数的开始执行前构造的,在成员的执行结束后清除。

3、this指针如何传递给类中函数的?绑定?还是在函数参数的首参数就是this指针,那么this指针又是如何找到类实例后函数的?

this是通过函数参数的首参数来传递的。this指针是在调用之前生成的。类实例后的函数,没有这个说法。类在实例化时,只分配类中的成员变量空间,并没有为成员函数分配空间。

this指针只有在成员函数中才有定义。因此,你获得一个对象后,也不能通过对象使用this指针。所以,我们也无法知道一个对象的this指针的位置(只有在成员函数里才有this指针的位置)。当然,在成员函数里,是可以知道this指针的位置的(可以&this获得)。
 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值