我是靠谱客的博主 甜美秀发,这篇文章主要介绍【( C++初阶 )----- 类与对象(1)】目录,现在分享给大家,希望可以做个参考。

目录

一、面向过程和面向对象初步认识

二、类的定义

1.类的两种定义方式

三、类的访问限定符及封装

1.访问限定符

四、封装

五、类的作用域

六、类的实例化

七、类对象模型

1. 计算类对象的大小

2.类对象的存储方式猜测

八、this指针

1. this指针的引出

2. this指针的特性

一、面向过程和面向对象初步认识

C 语言是 面向过程 的, 关注 的是 过程 ,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++ 基于面向对象 的, 关注 的是 对象 ,将一件事情拆分成不同的对象,靠对象之间的交互完成。
不是纯面向对象,可以面向对象和面向过程混编 兼容C语言
假设要设计简单学校外卖系统
面向过程:
关注:点餐、接单、送餐过程、关注流程函数的实现。
面向对象:
关注:用户、商家、骑手。关注对象之间关系。

二、类的定义

class className
{
	// 类体:由成员函数和成员变量组成

};  // 一定要注意后面的分号
class 定义类的 关键字, ClassName 为类的名字, {} 中为类的主体,注意 类定义结束时后面 分号不能省
类体中内容称为 类的成员: 类中的 变量 称为 类的属性 成员变量 ; 类中的 函数 称为 类的方法 或者 成员函数

1.类的两种定义方式

1. 声明和定义全部放在类体中,需注意:成员函数如果 在类中定义 ,编译器 可能 会将其当成 内联函数 处理。
class Stack
{
public:
	void Init()   //Init可能会被当成内联函数处理
	{
		a = 0;
		top = capacity = 0;
	}

	void Push(int x)
	{
		// ...
	}

	void Pop()
	{
		// ...
	}
private:
	int* a;
	int top;
	int capacity;
};

int main()
{
	Stack st;
	st.Init();
	st.Push(1);
	st.Push(2);
	st.Push(3);
}

 2. 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::

//f.h

class Queue
{
public:
	void Init();
	void Push(int x);
	void Pop();
private:
	QueueNode* head;
	QueueNode* tail;
};


//f.cpp

void Queue::Init()
{
	head = tail = nullptr;
}

void Queue::Push(int x)
{}

void Queue::Pop()
{}
类的定义:
1.小函数,想成为 inline ,直接在类里面定义即可
2.如果是大函数,应该声明和定义分离

 成员变量命名规则的建议:

1.单词和单词之间首字母大写间隔 ----- 驼峰法   GetYear

2.单词全部小写,单词之间_分割                       get_year

驼峰法:

a、函数名、类名等所有单词首字母大写  DateMgr

b、变量首字母小写,后面单词首字母大写  dateMgr

c、成员变量,首单词前面加_  _dateMgr

//一般都建议这样
class Date
{
public:
	void Init(int year)
	{
		_year = year;
	}
private:
	int _year;
};
// 或者这样
class Date
{
public:
	void Init(int year)
	{
		mYear = year;
	}
private:
	int mYear;
};

三、类的访问限定符及封装

1.访问限定符

C++ 实现封装的方式: 用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其 接口提供给外部的用户使用

 

【访问限定符说明】
1. public 修饰的成员在类外可以直接被访问
2. protected private 修饰的成员在类外不能直接被访问 ( 此处 protected private 是类似的 )  
3. 访问权限 作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. 如果后面没有访问限定符,作用域就到 } 即类结束。
5. class 的默认访问权限为 private struct public( 因为 struct 要兼容 C)
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别

问题: C++ struct class 的区别是什么?
解答: C++ 需要兼容 C 语言,所以 C++ struct 可以当成结构体使用。另外 C++ struct 还可以用来定义类。和 class 定义类是一样的,区别是 struct 定义的类默认访问权限是 public class 定义的类默认访问权限是 private

四、封装

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节(private/protected 成员变量),仅对外公开接口(public 成员函数)来和对象进行 交互。
封装本质上是一种管理,让用户更方便使用类
比如:对于电脑这样一个复杂的设备,提供给用户的就只有开关机键、通过键盘输入,显示器,USB 插孔等,让用户和计算机进行交互,完成日常事务。但实际上电脑真正工作的却是CPU 、显卡、内存等一些硬件元件。

对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的, CPU 内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时, 在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计 算机进行交互即可
C++ 语言中实现封装,使用 protected/private 把成员封装起来,开发一些共有的成员函数对成员合理的访问,所以 封装本质是一种管理
因此
C语言---> 没办法封装 既可以规范使用函数访问数据 也可以直接访问数据 --- 不规范
C++  --->  封装 必须规范使用函数访问数据  不能直接访问数据

五、类的作用域

类定义了一个新的作用域 ,类的所有成员都在类的作用域中 在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域
class Person
{
public:
	void PrintPersonInfo();  // 声明
private:
	char _name[20];          // 声明
	char _gender[3];
	int  _age;
};


// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()  // 定义
{
	cout << _name << " " << _gender << " " << _age << endl;
}

六、类的实例化

用类类型创建对象的过程,称为类的实例化
1. 类是对对象进行描述的 ,是一个 模型 一样的东西,限定了类有哪些成员,定义出一个类 并没有分配实际 的内存空间 来存储它;比如:入学时填写的学生信息表,表格就可以看成是一个类,来描述具体学生信息。
2. 一个类可以实例化出多个对象, 实例化出的对象 占用实际的物理空间,存储类成员变量
3. 做个比方。 类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图 ,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间

七、类对象模型

1. 计算类对象的大小

class A
{
public:
	void PrintA()
	{
		cout << _a << endl;
	}

	void func()
	{
		cout << "void A::func()" << endl;
	}
private:
	char _a;
};

int main()
{
	cout << sizeof(A) << endl;

	return 0;
}

运行结果为1,可知类的大小计算仅针对成员变量,成员函数不参与计算。

2.类对象的存储方式猜测

对象中包含类的各个成员

缺陷:每个对象中成员变量是不同的,但是调用同一份函数,如果按照此种方式存储,当一个类创建多个对象时,每个对象中都会保存一份代码,相同代码保存多次,浪费空间。那么如何解决呢?

代码只保存一份,在对象中保存存放代码的地址  

只保存成员变量,成员函数存放在公共的代码段 

class A
{
public:
	void PrintA()
	{
		cout << _a << endl;
	}

	void func()
	{
		cout << "void A::func()" << endl;
	}

	char _a;
};

int main()
{
	A aa1;
	A aa2;

	aa1._a = 1;
	aa2._a = 2;

	aa1.PrintA();//函数PrintA()的地址存储在公共代码区
	aa2.PrintA();

	return 0;
}	

我们可以了解到不同实例化的对象成员所调用的成员函数的地址是相同的,因此类中成员函数的地址是存储在公共代码区上的,编译链接时就根据函数名去公共代码区找到函数的地址(call函数地址)

 结论:一个类的大小,实际就是该类中成员变量之和,当然要注意内存对齐

注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。
//类中仅有成员函数
class A2 
{
public:
	void f2() {}
};

//类中什么都没有---空类
class A3
{};

int main()
{
	cout << sizeof(A2) << endl;
	cout << sizeof(A3) << endl;

	 没有成员变量的类对象,给1byte,占位不存储实际数据,标识对象存在
	A2 aa2;
	A2 aaa2;
	cout << &aa2 << endl;
	cout << &aaa2 << endl;

	return 0;
}

八、this指针

1. this指针的引出

class Date
{
public:
	void Print(Date* const this)
	{
		cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
	}

	void Init(int year, int month, int day)
	{
		_year = year;
        _month = month;
        _day = day;
	}

private:
	int _year;     // 年   -> 声明
	int _month;    // 月
	int _day;      // 日
};


int main()
{
	Date d1;
	d1.Init(2022, 7, 17);

	Date d2;
	d2.Init(2022, 7, 18);

	d1.Print();
	d2.Print();

	return 0;
}
Date 类中有 Init Print 两个成员函数,实例化的d1和d2是如何调用相同的函数,而不会引起差异呢?
C++ 中通过引入 this 指针解决该问题,即: C++ 编译器给每个 非静态的成员函数 增加了一个隐藏的指针参 数,让该指针指向当前对象 ( 函数运行时调用该函数的对象 ) ,在函数体中所有 成员变量 的操作,都是通过该 指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成

2. this指针的特性

1. this 指针的类型:类类型 * const ,即成员函数中,不能给 this 指针赋值。
2. 只能在 成员函数 的内部使用
void Init(int year, int month, int day)
{
		//this = nullptr;//error  this指针被const修饰

		cout << this << endl;
		this->_year = year;
		this->_month = month;
		this->_day = day;
}

注:

实参和形参位置不能显示传递和接收this指针,但是可以在成员函数内部使用this指针

3. this 指针本质上是 成员函数 的形参 ,当对象调用成员函数时,将对象地址作为实参传递给 this 形参。所以对象中不存储 this 指针

注:

this指针相当于函数形参,所以它存储在栈区中

VS下面传递this指针,是通过ecx寄存器传递的,这样this访问变量提高效率

4. this 指针是 成员函数 第一个隐含的指针形参,一般情况由编译器通过 ecx 寄存器自动传递,不需要用 户传递

3.经典例题

// 1.下面程序编译运行结果是?  A、编译报错  B、运行崩溃  C、正常运行 -- ok
// 2.this指针存在哪里?  堆  栈  静态区 常量区  -- 栈,因为他是一个形参
class A
{
public:
	void PrintA()
	{
		cout << this << endl;
		cout << _a << endl;
		cout << _a << endl;
		cout << _a << endl;
		cout << _a << endl;
		cout << _a << endl;
		cout << _a << endl;
	}

	void Print(int x)
	{
		cout << this << endl;
		cout << "Print()" << endl;
	}
private:
	int _a;
};

int main()
{
	A* p = nullptr;
	p->Print();      // C、正常运行 仅去调用公共代码区的函数,并没有对空指针解引用
	p->PrintA();     // B、运行崩溃,对空指针进行解引用

	A aa;
	aa.Print(1);


	return 0;
}

最后

以上就是甜美秀发最近收集整理的关于【( C++初阶 )----- 类与对象(1)】目录的全部内容,更多相关【(内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(64)

评论列表共有 0 条评论

立即
投稿
返回
顶部