不要觉的自己很没用,其实你还可以给家人带来温暖,比如爸妈看到你就来火
目录:
一、面向过程和面向对象初步认识
二、类的引入
三、类的定义
四、类的访问限定符及封装
1.访问限定符
2.封装
五、类的作用域
六、类的实例化
七、类的对象大小的计算
1.类对象的存储方式猜测
2.结构体内存对齐规则
3.为什么要进行内存对齐?
八、类成员函数的this指针
1.this指针的作用
2.this指针的特性
3.面试题练习
九、完结撒❀
前言:
对于类和对象的讲解一共划分为3篇,这是第1篇入门篇学习,更适合小白宝宝的体质
–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀-正文开始-❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–
一、面向过程和面向对象初步认识
二、类的引入
typedef int DataType;
struct Stack
{void Init(size_t capacity){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (nullptr == _array){perror("malloc申请空间失败");return;}_capacity = capacity;_size = 0;}void Push(const DataType& data){// 扩容判断 此处省略_array[_size] = data;++_size;}DataType Top(){return _array[_size - 1];}void Destroy(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}DataType* _array;size_t _capacity;size_t _size;
};int main()
{Stack s;s.Init(10);s.Push(1);s.Push(2);s.Push(3);cout << s.Top() << endl;s.Destroy();return 0;
}
可以看到在上面代码中,结构体stack里面定义了变量:
三、类的定义
类的定义如下:
class className
{// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
在后序工作中一般使用第二种定义方式
在上面演示图片中的代码,细心的同学可能已经注意到了,我们定义人的结构体变量名前面都加上了一个_,其实这是为了防止变量名冲突混淆含义而定义的
举例如下:
class Data
{
public://我们看这个函数会感觉很僵硬void Init(int year, int month, int day){//这里的year、month、day到底是成员变量还是函数形参year = year;month = month;day = day;}private:int year;int month;int day;
};
上面代码无法区分Init函数中谁是形参谁是变量
所以我们在实际中一般这样写:
class Data
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};
其他命名方式都可,这主要看公司要求,一般都是加一个前缀加一个后缀标识区分就行。
上面定义结构体中我们使用了public和private,这是类的访问限定符,下面我们对其进行间接。
四、类的访问限定符及封装
1.访问限定符
class Data
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};
上面代码,类中共有域为public到private的范围,私有域为private到类结束为止
那么我们在主函数中创建类变量即可访问到函数Init,但是访问不到类的成员变量,因为成员变量范围在私有域内,所以只能在类里面才能被访问
class Data
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};int main()
{Data da1;da1.Init(2024, 4, 7);//直接访问da1.Init(_day);//直接报错return 0;
}
之前看到的一道【面试题】:
2.封装
五、类的作用域
比如下面代码:
class Person
{
public:void PrintPersonInfo();
private:char _name[20];char _gender[3];int _age;
};// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{cout << _name << " "<< _gender << " " << _age << endl;
}
六、类的实例化
还拿上面代码举例:
class Data
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};int main()
{Data da1;da1.Init(2024, 4, 7);//直接访问da1.Init(_day);//直接报错return 0;
}
类中的成员变量_year,_month,_day都是对其进行的声明,并没有开辟真正的空间,只有当定义出类变量da1时才会真正对类中的成员变量开辟空间,这就是类的实例化。
七、类的对象大小的计算
class Person
{
public:void PrintPersonInfo();void SetPersonInfo();private:char _name[20];char _gender[3];int _age;
};
1.类对象的存储方式猜测
··· 对象中包含类的各个成员
··· 代码只保存一份,在对象中保存存放代码的地址
··· 只保存成员变量,成员函数存放在公共的代码段
问题:对于上述三种存储方式,那计算机到底是按照那种方式来存储的?
我们再通过对下面的不同对象分别获取大小来分析看下
class A1 {
public:void f1(){}
private:int _a;
};// 类中仅有成员函数
class A2 {
public:void f2() {}
};// 类中什么都没有---空类
class A3
{};
分别计算每个类的大小可以直到
sizeof(A1):4 sizeof(A2) : 1 sizeof(A3) : 1
A1中有一个成员函数和成员变量构成,总大小为4个字节
A2只有一个成员函数组成,总大小为1个字节
A3为空类,里面什么都没定义,总大小为1个字节
空类和只有一个成员函数的类大小相等都为1个字节,说明类的空间大小并没有计算成员函数的大小
那么我们就可以推测出
2.结构体内存对齐规则
下面我们再回顾学习一下结构体的内存对齐规则
3.为什么要进行内存对齐?
这里查询资料,大部分的参考资料都是这样说的:
1.平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2.性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需做两次内存访问;而对齐的内存访问仅需要一次访问。假设一个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用一个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。
总体来说:结构体的内存对齐是拿空间来换取时间的做法
八、类成员函数的this指针
1.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 main()
{Date d1, d2;d1.Init(2022,1,11);d2.Init(2022, 1, 12);d1.Print();d2.Print();return 0;
}
class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}//void Print()void Print(Data* const this){//cout << _year << "-" << _month << "-" << _day << endl;cout << this->_year << "-" << this->_month << "-" << this->_day << endl;}private:int _year; //年int _month; //月int _day; //日
};int main()
{Date d1, d2;d1.Init(2022, 1, 11);d2.Init(2022, 1, 12);d1.Print();d2.Print();return 0;
}
上面代码中this指针所在位置就是编译器自己进行处理的操作,因为是隐含的this指针,形参和实参的位置不能显示写,编译器会自行处理,但是this指针是可以在类里面显示着进行使用的。
我们需要知道的是函数体是进行对象区分是通过this指针自动将成员变量的地址传递了过去,当作函数的形参进行了使用。
2.this指针的特性
3.面试题练习
下面我们来看两道【面试题】
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:void Print(){cout << "Print()" << endl;}
private:int _a;
};int main()
{A* p = nullptr;p->Print();return 0;
}
答案:C、正常运行
题解:定义出的类指针变量赋值为空指针,之后访问Print成员函数,这里我们就要清楚程序是如何进行Print函数的访问的——通过this指针,而我们只是向Print函数传了一个空指针作为形参而已,并且我们并没有对空指针进行访问,所以不会出现报错,所以程序最终进入Print函数打印出了Printf()字符串。
根据下面代码选择ABC中一个选项:
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:void PrintA() {cout<<_a<<endl;}
private:int _a;
};int main()
{A* p = nullptr;p->PrintA();return 0;
}
答案:B
题解:上面代码中PrintA函数里cout<<_a<<endl;在编译运行时真正的运行的代码为:cout<<this->_a<<endl;而这里的this指针为空指针,对空指针访问成员变量最后程序就会运行崩溃。
九、完结撒❀
如果以上内容对你有帮助不妨点赞支持一下,以后还会分享更多编程知识,我们一起进步。
最后我想讲的是,据说点赞的都能找到漂亮女朋友❤