C++<初阶>——类和对象(上)

C++类与对象核心解析

1.访问限定符

三种访问限定符:public(公有)、private(私有)、protected(保护)

访问限定符:用于修饰成员,限定成员的权限。

一般类中的成员变量是被private(私有)修饰;成员函数是被public修饰。这样操作的好处是类中的成员变量只有类中的成员函数才可以访问,不可公开引用,成员函数可以由公开引用

访问限定符的作用域:从当前访问限定符开始,直到下一个访问限定符出现

(可以看完类的实例化部分再返回看这一块的代码)

#include<bits/stdc++.h>
using namespace std;

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 year;

int main()
{
    //访问私有的成员变量(若把注释部分去除,会报错,可以试试)
	//这里因为_year是私有的,所以不能在main里面访问并修改
	//Date::_year = 1;
	//year = 1;

	//d1._year;
    //d2._year;

	
	// 类实例化出对象
	Date d1;
	Date d2;
	d1.Init(2024, 9, 13);
	d2.Init(2025, 9, 14);

    //访问公有的成员函数
	d1.Print();
	d2.Print();

    //打印结果
	cout << sizeof(d1) << endl;
	cout << sizeof(d2) << endl;

	return 0;
}

2.类

定义类

  • 在c++中一般使用class来定义类,但是也可以使用c语言中的struct。c++中struct既可当做类(函数+变量)来使用,也可以当做一般的结构体(只有变量)一样使用。
  • 若无限定修饰符,使用class定义类时,默认修饰的限定符是private私有;struct默认为public公有
  • 默认类里面的函数是inline函数(内联函数)

下面是一个类的示例

#include<bits/stdc++.h>
using namespace std;

//一个模拟栈类
class Stack
{
//公有化(类外可以访问到)
public:
	void Init(int n = 4)//缺省参数
	{
		arry = (int*)malloc(sizeof(int*) * n);
		if (nullptr == arry)
		{
			cout << "创建失败";
			return;
		}
		capcity = n;
		top = 0;
	}

	//取栈顶
	int Top()
	{
		if (top > 0)
			return arry[top-1];
	}

	//插入数据
	void Push(int x)
	{
		arry[top] = x;
		top++;
	}

	void arry_length()
	{
		for(int i = 0;i<top;i++)
		cout << arry[i];
		cout << endl << top;
	}
	

	//销毁数据
	void Destroy()
	{
		free(arry);
		arry = nullptr;
		capcity = 0;
	}

//私有化(只能在类里面访问到)
private:
	//声明成员变量
	int* arry;
	size_t capcity;
	size_t top;
};


int main()
{
	Stack st1;
	st1.Init();
	st1.Push(1);
	st1.Push(2);
	st1.Push(3);
	st1.Push(4);
	cout << st1.Top()<<endl;
	st1.arry_length();
	
	st1.Destroy();
	
	

	return 0;
}

3.类的实例化

区别是否声明主要看是否给其分配空间。类是对象的抽象概念,类里面只是声明了有哪些函数和变量,实例化就是创建了类的对象。举例来说,建房子,类就像是一个工程图纸,实例化就是根据图纸来分配空间建造一栋房子,可以使用这张图纸来建造许多相同的房子。也就是一个类可以抽象出许多个实例化对象,类无法储存数据,实例化才能储存数据。

3.1实例化对象的大小

  • 类的实例化对象大小只看实例化给的成员变量的字节数,不会加上成员函数的字节数。即类实例化时,只会存储成员变量,不会存储成员函数。
  • 对于没有成员变量的类,即只有成员函数,但是没有成员变量;甚至说是成员变量和函数都没有。因计算类对象大小时不计算成员函数的的大小,只计算成员变量的大小,那是否这个类对象的大小是0呢?🧭不是的,没有成员变量的类对象(甚至什么都没有的类)仍为其分配1byte空间大小,用来占位,但不存储有效数据,用于标识对象的存在。简言之:没有成员变量的类对象,大小为1byte,用来占位,但不存储有效数据

计算实例化类对象的大小:

#include<bits/stdc++.h>
using namespace std;

//定义一个Date类
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 year;

int main()
{
	/*Date::_year = 1;
	year = 1;*/

	// 1->N
	// 类实例化出对象
	Date d1;
	Date d2;
	d1.Init(2024, 8, 6);
	d2.Init(2025, 8, 7);

	//d1._year;
	//d2._year;

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

	cout << sizeof(d1) << endl;
	cout << sizeof(d2) << endl;

	return 0;
}

根据运行的结果可以看到:d1,d2的字节数都是12个字节,d1和d2都有三个成员变量且都是int类型,3*4=12字节,都是12个字节,所以实例化的对象的大小计算,只计算成员变量的大小,不计算成员函数的大小。

下面的有成员变量和成员函数的类A;只有成员函数的类B;什么都没有的类C

#include<bits/stdc++.h>
using namespace std;
 //计算一下A/B/C实例化的对象是多大?
class A
{
public:
	void Print()
	{
		cout << _ch << endl;
	}
private:
	char _ch;
	int _i;
};

// 没有成员变量的类对象,开1byte,占位,不存储有效数据
// 标识对象的存在
class B
{
public:
	void Print()
	{
		//...
	}
};

class C
{};

int main()
{
	A a;
	B b;
	C c;
	cout << sizeof(a) << endl;
	cout << sizeof(b) << endl;
	cout << sizeof(c) << endl;
	cout << &b << endl;
	cout << &c << endl;


	return 0;
}

根据结果可以看到没有成员变量的类对象的大小时1个字节

3.2成员函数和变量的保存位置

🧭成员函数的指针是在编译时就已经确定,没有存在对象中;但是成员变量是存在于对象中。

下面两个代码就可以体现出来

//第一个代码

#include<iostream>
using namespace std;

class A
{
public:
 void Print()
 {
     cout << "A::Print()" << endl;
 }
private:
     int _a;
};


int main()
{
     A* p = nullptr;
     p->Print();
     return 0;
}
//第二个代码

#include<iostream>
using namespace std;

class A
{
public:
 void Print()
 {
     cout << "A::Print()" << endl;
     cout << _a << endl;
 }
private:
     int _a;
};

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

第一个代码运行正常,第二个代码运行崩溃。乍一看,两个代码都是野指针的错用,应该两个都会崩溃?但为什么第一个正常……?🧭这是因为,在编译时,就已经将成员函数确定好了,那么在运行时,第一个代码虽然使用p->Print,但是实际上,这个Print中的cout结果并不会根据p进行任何变化,编译时就是确定好的,所以运行正常;但是第二个代码,Print函数中有cout<<_a,因为_a是成员变量,成员变量是保存在对象中,所以当对象指针变为nullptr时,就找不到这个对象,输出cout<<_a时就会崩溃。

3.2内存对齐的规则

内存对齐情况,虽然浪费了些许空间,但是内存对齐的情况下,若需要访问某一个或某些变量,访问的速度更快。

对齐                                     未对齐

🧭下图就是内存对齐的好处,一次读取读取四个字节。若访问_i,未对齐的情况下,需要两次读取才能完全读取到_i,拼好据;然而,在对齐的情况下,只需要读取一次数据,即可完整的读取到_i这个数据。若是放大这种情况,内存对齐下的访问速度是没未齐的两倍,快!

4.this指针

  • Date类中有Init与Print两个成员函数,函数体中没有关于不同对象的区分,那当d1调⽤Init和 Print函数时,该函数是如何知道应该访问的是d1对象还是d2对象呢?那么这⾥就要看到C++给了 ⼀个隐含的this指针解决这⾥的问题

  • 编译器编译后,类的成员函数默认都会在形参第⼀个位置,增加⼀个当前类类型的指针,叫做this 指针。⽐如Date类的Init的真实原型为, void Init(Date* const this, int year, int month, int day) 

  • 类的成员函数中访问成员变量,本质都是通过this指针访问的,如Init函数中给_year赋值, this- >_year = year;

  • C++规定不能在实参和形参的位置显⽰的写this指针(编译时编译器会处理),但是可以在函数体内显 ⽰使⽤this指针。

#include<iostream>
using namespace std;

class Date
{
public:
 // void Init(Date* const this, int year, int month, int day)
 void Init(int year, int month, int day)
 {
     // 编译报错:error C2106: “=”: 左操作数必须为左值 
     // this = nullptr;
     // this->_year = year;
     _year = year;
     this->_month = month;
     this->_day = day;
 }
 void Print()
{
     cout << _year << "/" << _month << "/" << _day << endl;
 }
private:
     // 这⾥只是声明,没有开空间 
     int _year;
     int _month;
     int _day;
};

int main()
{
     // Date类实例化出对象d1和d2 
     Date d1;
     Date d2;

     // d1.Init(&d1, 2024, 3, 31);
     d1.Init(2024, 3, 31);
     d1.Print();
     d2.Init(2024, 7, 5);
     d2.Print();
     return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jc导航键

感谢您的认可,这是我更新的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值