类和对象的介绍一

目录

1.面向过程与面向对象的初步认识

2.类的引入

3.类的定义

4.类的访问限定符及封装

5.类的作用域

6.类的实例化

7.类的对象的大小的计算

8.类成员函数的this指针


1.面向过程与面向对象的初步认识

面向对象和面向过程是两种编程思想。面向过程是一种以事件为中心的编程思想,编程的时候把解决问题的步骤分析出来,然后用函数把这些步骤实现,在一步一步的具体步骤中再按顺序调用函数。

而面向对象是一种以“对象”为中心的编程思想,把要解决的问题分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个对象在整个解决问题的步骤中的属性和行为。

面向过程和面向对象各有优缺点。面向过程强调代码的短小精悍,善于结合数据结构来开发高效率的程序。但需要深入思考,耗费精力,代码重用性低,扩展能力差,后期维护难度比较大。而面向对象程序设计中代码间的相关性低(低耦合特性),使得代码很容易被复用和扩展,同时也说明了面向过程的代码重用性低、扩展能力差。但开销大,当要修改对象内部时,对象的属性不允许外部直接存取,所以要增加许多没有其他意义、只负责读或写的行为。这会为编程工作增加负担,增加运行开销,并且使程序显得臃肿。

2.类的引入

  C语言结构体中只能定义变量,在 C++ 中,结构体内不仅可以定义变量,也可以定义函数。比如:
之前在数据结构初阶中,用C 语言方式实现的栈,结构体中只能定义变量 ;现在以 C++方式实现,
会发现struct中也可以定义函数,对于这样的结构体,c++中升级成了类。 
因为c++兼容c,一个结构体我们利用struct创建变量,也可以直接结构体名加变量来创建。
typedef int datatype;
//利用结构体定义栈,内部可定义成员函数
struct Stack
{
	//初始化
	void stinit(int _capacity)
	{
		arr = (datatype*)malloc(sizeof(datatype) * _capacity);
		if (arr == nullptr)
		{
			return;
		}
		 size= 0;
		 capacity = _capacity;
	}
	//入栈
	void stackpush(const datatype& _data)
	{
		arr[size] = _data;
		size++;
	}
	void stackpop()
	{
		size--;
	}
	datatype gettop()
	{
		return arr[size-1];
	}
	void destroyst()
	{
		if (arr!= nullptr)
		{
			free(arr);
			arr = nullptr;
			size = capacity = 0;
		}
	}
	int* arr;
	int size;
	int capacity;
};

int main()
{
	struct Stack ST;
	ST.stinit(5);
	ST.stackpush(1);
	ST.stackpush(2);
	ST.stackpush(3);
	ST.stackpush(4);
	cout << ST.gettop() << endl;
	ST.stackpop();
	cout << ST.gettop() << endl;
	return 0;
}

如上我们定义了成员函数和成员变量,可以值间接调用成员函数。在这里struct 已经作为一个类在用了。

3.类的定义

在上述我们知道了struct 也可作为类,那么类的定义是什么?什么样的结构叫做类?

其实类是一种用户自定义的数据类型,它描述了一组有相同特性(属性)和相同行为(方法)的一组对象的集合。一个类的定义通常包含两部分的内容,一是该类的属性,另一部分是它所拥有的方法。这样的结构类型就可叫做类。

而对于c++,除了常用的结构体是类,c++也提供了一个关键字class专门用来定义类。这也是大多数人选择的方法。

class className
{
// 类体:由成员函数和成员变量组成
};  // 一定要注意后面的分号
class 定义类的 关键字, ClassName 为类的名字, {} 中为类的主体,注意 类定义结束时后面
号不能省略
类体中内容称为 类的成员: 类中的 变量 称为 类的属性 成员变量 ; 类中的 函数 称为 类的方法 或者
成员函数
类的两种定义方式:
1. 声明和定义全部放在类体中,需注意:成员函数如果 在类中定义 ,编译器可能会将其当成
联函数 处理。
2. 类声明放在 .h 文件中,成员函数定义放在 .cpp 文件中,注意: 成员函数名前需要加类名 ::。
这与我们之前实现数据结构类似,函数的实现我们一般都放在.c文件当中,c++也如此。

4.类的访问限定符及封装

同样是类,为什么在选择类的定义是,人们一般不用struct,而取用clss,这里就要说到一点他们的访问限定符的区别。

在C++中,可以使用以下访问修饰符在进行声明时指定类型或成员的可访问性:public、protected、private。

类和对象的介绍一_第1张图片

.public: 可以被该类中的函数、子类的函数、其友元函数访问,也可以由该类的对象访问
.protected: 可以被该类中的函数、子类的函数、以及其友元函数访问,但不能被该类的对象访问。
.private: 只能由该类中的函数、其友元函数访问,不能被任何其他访问,该类的对象也不能访问
而对于我们刚才说到的struct,它的默认访问限制是public,外部都是可以访问的。而对于class,它的默认访问限制是private,一般除了类中的是无法访问的。而对于真正实现大项目时,为了防止相互可能存在访问的情况下,private对于程序员来说更好。这就是为什么大多数人选择class,其次在继承和模板参数列表位置,structclass也有区别。

 注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别

封装:

首先,面向对象的三大特性分别是:封装、继承、多态
在类和对象阶段,主要是研究类的封装特性,那什么是封装呢:
封装是指一种将抽象性函数接口的实现细节部分包装、隐藏起来的方法。封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。要访问该类的代码和数据,必须通过严格的接口控制。封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。

在C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来
隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。

对于类的内联函数,还是一样,函数不会进入符号表,直接展开。

5.类的作用域

对于类和一般定义的空间的内部的访问,他们一般都是有空间访问限制的,这就需要在访问时加上结构::成员,像这种结构就跟之前学的namespace的定义一样,在实现struct class这种结构时,其实也定义了空间。是不被外部访问的,需要借助域访问作用符。

6.类的实例化

类是对对象进行描述的 ,是一个 模型 一样的东西,限定了类有哪些成员,定义出一个类 并没
有分配实际的内存空间 来存储它;

定义的类就是一种类型,我们可以通过这种类实例化多个对象。

class  Person
{
 
public:
	//方法一
	//方法二
	//方法三 
private:
    //他的属性
	char* name;
	int age;
	int scoer;
	int id;

};
int main()
{
	Person p1;
	Person p2;
	Person p3;

}
类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图 ,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象 才能实际存储数据,占用物理空间。
类和对象的介绍一_第2张图片

 

对于一个类,它的变量只是声明,所有的成员函数不在类中存储且只是声明,它们会被提供一块公共的代码区域,一面每个对象都能访问且不同。

7.类的对象的大小的计算

      一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐,注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。
      类的大小计算遵循结构体的对齐原则, 与普通数据成员有关,与成员函数和静态成员无关。虚函数和虚继承对类的大小有影响,是因为虚函数表指针和虚基表指针带来的影响。空类的大小为1。类只是一个类型定义,它是没有大小可言的。用sizeof运算符对一个类型名操作,得到的是具有该类型实体的大小。当类不包含虚函数和非静态数据成员时,其对象大小也为1。虚函数本身和其他成员函数一样,是不占用对象的空间的
class A1 {
public:
	void f1() {}
private:
	int _a;
	char c;
};
//只有成员函数
class A2
{
public:
	void f2() {}
	void f3() {}
};
// 类中什么都没有---空类
class A3
{};
int main()
{
	cout << sizeof(A1) << endl;
	cout << sizeof(A2) << endl;
	cout << sizeof(A3) << endl;
	return 0;
}

 根据编译的结果可以看到成员函数实际上是不计入大小的,只记入成员变量。空类认为是1。

结构体内存对齐规则
1. 第一个成员在与结构体偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的对齐数为8
3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
根据类的大小我们可以看出成员函数的栈帧是不在类里定义的。
内存对齐的设计是为了便于访问。

8.类成员函数的this指针

1.this指针的特性:类型:类类型*const,在成员函数中无法赋值,无法作为实体参数。

2.只能在成员函数中使用

类和对象的介绍一_第3张图片

 类和对象的介绍一_第4张图片

实际上是通过传入对象的指针来确定对函数的调用,类中定义的仅仅是个声明,不可能定义多个对象都去访问类中的变量。

其次我们虽然知道是有个this指针,但不可以直接传个对象指针作为this指针,this指针可以在函数内部访问成员变量会其他成员函数,但不能实体化作为参数,他是隐形的。

 注意事项:

类和对象的介绍一_第5张图片

上述三种给三种情况 ,A .编译错误  B.运行崩溃  C.正常运行

对于第一个,可以看到定义了一个对象指针 且为空,之后利用对象指针访问函数。首先我们会想到空指针访问需要解引用,但不会报编译错误,排除A,但实际上选C,正常运行:因为成员函数不存放在类中且也不存放在对象中,(存在对象中会是对象占良大量空间),成员函数实际是存在一块公共代码区域的,调用函数直接拿函数名去找这个函数的地址,因此并会对对象指针解引用,直接去访问函数。

第二个,与第一个原理类似,这里也不会报错,即使我们这样去写,但编译器不会解引用指针,也是直接那函数名去公共代码空间哪里找。

第三个,会运行崩溃,可以发现打印中会去访问对象的成员变量,空指针会去解引用,因此会运行崩溃。

其次在vs传递this指针时是通过寄存器来传递的。

你可能感兴趣的:(数据结构,算法)