C++类与对象入门

首先我们来了解一下对象的相关知识
C语言是面向过程的,关注的是 过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++是 基于面向对象 的, 关注 的是 对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
但是C++不是纯面向对象,可以面向对象和面向过程混编,C++是兼容C的。
什么是面向对象什么是面向过程?
例如我们经常使用的外卖软件,面向过程指的就是重点关注我们下单,商家接单,骑手送餐关注这些流程具体如何实现
面向对象就是关注客户,商家,骑手这三个对象之间的关系
例如C中栈的实现,C关注的是如何实现这些函数功能
struct Stack
{
	int* a;
	int top;
	int capacity;
};

void StackInit(struct Stack* ps);
void StackPush(struct Stack* ps, int x);
void StackPop(struct Stack* ps);

在C++中是兼容C的,但是C++把struct升级成了类

类里面可以定义成员变量,成员函数

struct Stack
{
	void Init()
	{
		a = nullptr;
		top = capacity = 0;
	}
	
	void Push(int x)
	{
		// ...
	}

	void Pop()
	{
		// ...
	}

	int* a;
	int top;
	int capacity;
};
int main()
{
	struct Stack st1;
	Stack st2;

	st1.Init();
	st1.Push(1);
	st1.Push(2);
	st1.Push(3);


	return 0;
}

C++可以这样写,因为ListNode既是类名也是类型

struct ListNode_CPP
{
	ListNode_CPP* next;
	int val;
};

类的定义

C++中使用calss进行类定义

访问限定符:共有,保护,私有

1. public 修饰的成员在类外可以直接被访问
2. protected private 修饰的成员在类外不能直接被访问 ( 此处 protected private 是类似的 )
3. 访问权限 作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. class 的默认访问权限为 private struct public( 因为 struct 要兼容 C)

class Stack
{
public:
	void Init()
	{
		a = 0;
		top = capacity = 0;
	}
	
	void Push(int x)
	{
	}

	void Pop()
	{
	}

	int Top()
	{
		return a[top-1];
	}
 private:
	int* a;
	int top;
	int capacity;
};

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


	st.a = nullptr;//无法访问,因为是私有,类外无法访问,但是类里面的可以访问


	return 0;
}
类的两种定义方式:
1. 声明和定义全部放在类体中,需要注意:成员函数如果 在类中定义 ,编译器可能会将其当成 内联函数处理(函数里面有递归或者代码过长就不会按内联函数处理)。
2. 声明放在 .h 文件中,类的定义放在 .cpp 文件
//.h
struct QueueNode
{
	QueueNode* next;
	int val;
};


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

//.cpp
//类是一个域影响访问,使用::
void Queue::Init()//要说清楚是哪的,不然链接不上,告诉是类里的,去类里面找
{
	head = tail = nullptr;
}

void Queue::Push(int x)
{}

void Queue::Pop()
{}
小函数,想成为 inline ,直接在类里面定义,防止链接错误
大函数,声明和定义分离
C语言,没办法封装,规范使用函数访问数据,也可以直接访问数据  不规范
C++,经过封装,必须规范使用函数访问数据,不能直接访问数据
在上面之所以使用 ::是因为
类定义了一个新的作用域 ,类的所有成员都在类的作用域中 在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
那么我们观察以下代码
int a;//定义
class Person
{

public:

 void Print();//这里是声明

private:

 char _arr[20];//这里是声明还是定义 ?
 char _age[3];//这里是声明还是定义 ?
 int _year;//这里是声明还是定义 ?

};

答案是下面的是声明,那么为什么不是定义

因为此时类并没有进行实例化,也就是说没有进行Person hh;没有进行开辟空间,开辟了空间才能称之为定义

此时我们使用sizeof(Person)验证一下,计算结果为28,既然我们没有实例化,没有实际开辟空间为什么会算出来28这个结果,这就好比我们买房子时,我们的房子并没有建好,但是我们可以看到房子的图纸,根据图纸我们就可以知道我们的房子面积有多大

这里计算类对象的大小也是遵循C中结构体对齐规则的

结构体内存对齐规则
1. 第一个成员在与结构体偏移量为 0 的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的对齐数为 8
3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

没有成员变量的对象,给1byte,占位不存储实际数据,标识对象存在

#include<iostream>
using namespace std;
//extern int a;
static int a;
class Person
{

};

int main()
{

	cout << sizeof(Person) << endl;

	return 0;
}

this指针

class Date
{ 
public:
 void Init(int year, int month, int day)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 void Print()
 {
 cout <<_year<< "-" <<_month << "-"<< _day <<endl;
 }
private:
 int _year; // 年
 int _month; // 月
 int _day; // 日
 int a;
};

int main()
{
 Date d1, d2;
 d1.Init(2022,1,11);
 d2.Init(2022, 1, 12);
 d1.Print();
 d2.Print();
 return 0; 
}

我们运行上面的程序

Date 类中有 Init Print 两个成员函数,函数体中没有关于不同对象的区分,我们创建了d1,d2两个对象,但是当 d1 调用 Init 函数时,该函数怎么知道调用的时d1 对象,而不是 d2 对象,不会发生冲突
这里就涉及到this指针的问题,下面我们把this指针展示出来给大家看一下
public:
 void Init(Data*const this,int year, int month, int day)
 {

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

 void Print(Data*const this)
 {
     cout <<this->_year<< "-" <<this->_month << "-"<< this->_day <<
     endl;

 }

这里的this指针编译过程不会显示出来,使用时只可以在函数内部加this- >,在其它地方加错误(实参和形参位置不能显示传递和接收this指针)(可以在成员函数内部使用this指针)

d1.Init(&d1,2022,1,11);//正常使用时不用&d1,下同
d2.Init(&d2,2022,1,12);
d1.Print(&d1);
d2.Print(&d2);
C++ 编译器给每个 非静态的成员函数 增加了一个隐藏的指针参 数,让该指针指向当前对象 ( 函数运行时调用该函数的对象 ) ,在函数体中所有 成员变量 的操作,都是通过该 指针去访问。只不过所有的操作对用户是隐形的,即用户不需要来传递,编译器自动完成
this指针的性质总结
1. this 指针的类型:类类型 * const ,即成员函数中,不能给 this指针赋值。
2. 只能在 成员函数 ”的内部使用。
3. this 指针本质上是 成员函数 的形参 ,当对象调用成员函数时,将对象地址作为实参传递给 this 形参。 所以对象中不存储 this 指针。
4. this 指针是 成员函数 第一个隐含的指针形参,一般情况由编译器通过 ecx 寄存器自动传递,不需要用户传递
那么this指针是否可以为空?
class A {
public:
 void Print()
 {
 cout << "Print()" << endl;
 }
private:
 int _a;
};

int main()
{
 A* p = nullptr;
 p->Print();
 return 0; 
}

运行上述代码是可以正常运行的,首先p虽然是空指针,但是我们并没有解引用,其次这里的this指针虽然也是空指针,但是我们并没有使用它进行任何操作,至于为什么怎么调用的Print函数,是通过符号表call函数地址来调用的,下面这种情况就不一样了

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{ 
public:
 void PrintA() 
 {
 cout<<_a<<endl;
 }
private:
 int _a;
};

int main()
{
 A* p = nullptr;
 p->PrintA();
 return 0; 
}

这里运行就会崩溃了,原因是cout<<_a<<endl;这里cout<<this->_a<<endl,用空指针访问

this指针一般存在栈,他是一个形参也可能在寄存器(VS通过ecx寄存器传递,this访问变量提高效率)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值