以下是个人的学习笔记,如果有有些许出错的地方欢迎在评论区指出,如果有什么疑问,也可以在评论区问,我会尽力解答.
1.类的引入
C++兼容C语言,struct以前的用法都可以继续用,同时struct升级成了类
以一个Stack结构体为例:
#include<iostream> using namespace std; struct Stack { int* a; int top; int capacity; }; int main() { //C语言的结构体定义 struct Stack st1; //C++的类定义 Stack st2; return 0; }类当中,不仅可以有成员变量,还能有成员函数,
这个成员函数的好处是可以与其他类的成员函数重名,比如你要同时写栈,队列,链表...你就可以在类内直接写Init()初始化函数,而不是为了避免重名而写StackInit(),QueueInit(),ListInit()....
因为:类定义的也是一个域,所以函数可以同名(在不同的域中)
PS:C++中每个{}都是一个域.
实例代码如下:
#include<iostream> using namespace std; struct Stack { //成员函数 void Init() {//初始化,可以区别于队列,链表...的初始化 //... } void push(int x) { //... } //成员变量(与成员函数的位置没有要求, // 把整个类看成了一个整体,不用担心找不到) int* a; int top; int capacity; }; int main() { struct Stack st1; //成员函数调用 st1.Init(); st1.push(10); Stack st2; st2.Init(); st2.push(20); return 0; }2.类的定义
都说结构体变成类了嘛,那类的定义就跟结构体一样了
//类的定义 class className { //类体:由成员函数和成员变量组成 };//一定要注意后面的分号3.访问限定符
既然说,结构体变成了类,那我直接用类的方式声明结构体好不好?
#include<iostream> using namespace std; class Stack1 { void Init(){} void push(int x) {} int* a; int top; int capacity; }; int main() { class Stack1 st1; st1.Init(); st1.push(10); Stack1 st2; st2.Init(); st2.push(20); return 0; }看起来没毛病,但是你运行一下呢?你会发现编译通过不了,如下图
这是因为:C++新增了三种访问限定符
1.public(公有)-------可以在类外面直接访问
2.protect(保护)------在类外面不能直接访问
3.private(私有)------在类外面不能直接访问(在C++学习前期(继承之前)可以把2,3看成一种
因为类内的成员变量和成员函数,默认是private,不能在类外使用,所以才访问受限,不能编译
为什么结构体struct就可以呢?因为struct默认是public,(跟class还是有点区别的哈)
那怎么才能在类外使用class内的函数呢?只要加上访问限定符就可以了,像这样:
#include<iostream> using namespace std; class Stack1 { public: void Init(){} void push(int x) {} private: int* a; int top; int capacity; }; int main() { class Stack1 st1; st1.Init(); st1.push(10); Stack1 st2; st2.Init(); st2.push(20); return 0; }访问限定符的使用方法显而易见,就是直接加冒号:使用,而他的范围呢?
范围:一直作用到下一个访问限定符或类结束(没有下一个访问限定符了)
那为什么会存在这些访问限定符呢?
这就涉及到了面向对象语言的三大特性:封装,继承,多态,---其中的封装
封装,简而言之就是,把数据和方法放在一起,想给你看的就变成公有,不想给你看的就变成私有.
封装的本质是一种管理,方便用户使用.
他只把想要给你使用的权限给你,让你能够直接使用权限达到某种目的,但是不告诉你达到目的的过程,也不给你权限修改这个过程,比如让你使用某个函数,但是这个函数需要很多复杂的中间变量,一旦给你这些中间变量的访问权限,你在使用的过程中万一不小心修改,那这个函数的效果就可能跟预期不同了,所以他就会把这些变量保护起来.----通过使用访问限定符.
4.成员函数的定义和声明
既然是函数,就有定义和声明,类的函数在类内,(定义和声明都在类域内),方法跟普通函数在全局域内的定义和声明是一样的,如果类的函数的定义和声明不都在类域内呢?(声明肯定一定是要在类域内的嘛,不然你凭啥说他是类的成员函数.)
类的成员函数在类外的定义如下:
class Stack1 { public: void Init();//声明 void push(int x) {} private: int* a; int top; int capacity; }; void Stack1::Init() {}//定义5.类的inline函数
成员函数直接在类里面定义,默认就是inline函数,不管你加不加inline标识符
所以,长的成员函数,我们就要在类内声明,类外定义,短的成员函数我们可以在类内声明+定义
这时候就有人问了:牛霸牛霸,我不小心把长的成员函数也写在了类里面咋办?没关系!你的成员函数能否成为真正的inline,又不是你来决定的,你只是提个建议,编译器会帮你否决的
PS:补药搞事情,在类内加inline声明函数,却在类外定义,inline函数的声明和定义最好放在一个域内
6.成员变量的命名
我们平时可能会写出这样的代码
class Date { public: void Init(int year) { year = year; } private: int year; int month; int day; };看似没毛病,但是你初始化的时候,你的year到底是year呢,还是year呢?我也不知道啊,虽然这样可以过编译,但可能会出现问题啊,所以,你可以稍微修改一下成员变量的名字,比如这样:
class Date { public: void Init(int year) { _year = year; } private: int _year; int _month; int _day; };7.类实例化对象/对象定义
声明和定义的区别:声明不开空间,定义开空间
以下只是一个类的声明,而不是定义,没有开空间,里面的成员都不能使用
class Stack { public: void Init(){} void push(int x) {} private: int* a;//声明 int top; int capacity; };类的定义,也叫实例化,方式如下:
int main() { //类实例化对象/对象定义 Stack st; return 0; }创建了st的实例之后,就可以使用Stack的成员了,
可以将类的声明看作一个图纸,类的定义看作根据图纸建造房子,只有建造了房子之后,才能住人,也就是使用成员
8.对象的存储
你猜猜以下代码的结果:
cout<<sizeof(st)<<'\n';结果:
对象的大小只储存成员变量,没有储存成员函数
PS:这里的16并不是单纯的8(地址)+4(int)+4(int)=16;对于这种类的成员变量的存储,因为计算机要保证访问数据的效率,所以会涉及内存对齐的问题,(等我以后有时间写了相关的博客,会把链接放在这......)
class Stack { public: void Init(){} void push(int x) {} int* a;//声明 int top; int capacity; }; int main() { Stack st1; Stack st2; st1.top = 0; st2.top = 1; st1.Init(); st2.Init(); return 0; }思考一下,以上代码,st1的top和st2的top是不是同一个top?st1的Init()和st2的Init()是不是同一个Init()?
答案第一个是否定的,而第二个,是肯定的,第一个很好理解,第二个嘛,是因为,他们的函数是一样的,
单独存储在对象内,可以,但是太占用空间,于是就存储在了公共的区域,以方便节省空间.
就比如成员变量是家里的家具,而成员函数就是加油站,学校之类的,可以使用的公共区域,没必要在自己家里建一个加油站和学校,只需要在家外面建造一个一起使用就可以.