个人主页:Lei宝啊
愿所有美好如期而遇
目录
1.面向过程和面向对象初步认识
2.类的引入
3.类的定义
4.类的访问限定符及封装
5.类的作用域
6.类的实例化
7.类的对象大小的计算
8.类成员函数的this指针
1.面向过程和面向对象初步认识
在C语言中,除了库中的函数,你会发现我们想实现些什么都要自己写,而且还有很多不方便的地方,这样那样的缺陷和不足,相信很多人都比较难受,我们C++的祖师爷他也难受,所以才开创了C++。
C语言就是一门面向过程的语言,什么叫面向过程,就是注重过程,好比洗衣服,整个流程自己弄,相当于让你写篇小作文,详细写你是如何洗衣服的。而面向对象,就是你妈妈让你洗衣服,问你洗完衣服了吗,你说我用洗衣机洗完了。
总结:
2.类的引入
#include <iostream>
using namespace std;
struct S
{
int a = 1;
int top = 0;
int Add(int x, int y)
{
return a + x + y;
}
};
int main()
{
S a;
cout << a.Add(1, 2) << endl;
return 0;
}
当然,C++中类更喜欢用class来表示,而且class与struct有一点区别在于struct默认域里的成员函数和成员变量公有,而class默认私有,这个后面会说。
3.类的定义
class className{// 类体:由成员函数和成员变量组成}; // 一定要注意后面的分号
类的两种定义方式:1. 声明和定义全部放在类体中,需注意:成员函数如果 在类中定义 ,编译器可能会将其当成 内 联函数 处理。
class person
{
public:
void COUT(int a)
{
_a = a;
cout << "这是个人" << endl;
}
private:
int _a;
int _b;
int _c;
};
我们需要理解的一点是,类是一个整体,我们_a的声明虽然在后面,但是COUT函数在函数里找不到_a的时候,会在整个类里去找,这是编译器的一种搜索规则,namespace同理。
也就是说,在类里,同时支持向上和向下搜索,而C语言只支持向上。
pubilc和private一会儿会提到,莫急,往下看。
2. 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意: 成员函数名前需要加类名 ::
一般来说,函数体内的代码超过20行,编译器就不会采纳你的建议,将他当成内联函数了,所以我们下面的函数,100行代码的函数也写在类里的话,对于我们查看类里的内容,比如说各种声明时就不方便,而且编译器也不会将他当成内联函数,吃力不讨好,就可以考虑写在外面。
class person
{
private:
int _a;
int _b;
int _c;
public:
void COUT()
{
cout << "哈哈" << endl;
}
void crush()
{
//...假设这里有100行代码
}
};
int main()
{
person a;
for (int i = 0; i < 100; i++)
{
a.crush();
}
return 0;
}
写在外面:
class person
{
//即使我们不写private,class里也会默认他是private
//但最好写上,可读性会好些
private:
int _a;
int _b;
int _c;
public:
void COUT()
{
cout << "哈哈" << endl;
}
//假设这个函数有100行代码
void crush();
};
void person::crush()
{
//100行代码...
}
COUT函数可以写在里面 ,因为他函数体里代码并不多,也适合作为内联函数,内联函数不适合递归,适合代码量少但又频繁使用的情景。
成员变量命名规则的建议
class date
{
public:
//这里的year到底是哪个year呢》成员变量还是参数??
void Init(int year, int month, int day)
{
year = year;
month = month;
day = day;
}
int year;
int month;
int day;
};
int main()
{
date a;
a.Init(1, 2, 3);
cout << a.year << a.month << a.day << endl;
return 0;
}
并没有初始化a对象里的year,month和day。
所以我们需要标识类里的成员变量。
class date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
int _year;
int _month;
int _day;
};
int main()
{
date a;
a.Init(1, 2, 3);
cout << a._year << a._month << a._day << endl;
return 0;
}
C++里习惯给成员变量名字前或者后加上下划线标识,这不是标准,也不是规定,只是一种习惯,当然也有人会这样mYear,都可以,看你在哪家公司了。
4.类的访问限定符及封装
访问限定符C++ 实现封装的方式: 用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选 择性的将其接口提供给外部的用户使用
访问限定符:
public:(公有)
protect:(保护)
private:(私有)
现阶段我们可以将保护等同于private,区别在继承那个知识点会说到。现在我们用private就可以。
封装:封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来 和对象进行交互。
也就是说可以使用类里公开的方法实现你想实现的,但是如何实现你不知道,也无法直接修改和查看类里私有的成员变量。
好比一个计算机,你可以使用计算机算数,但是他如何实现,以及工作原理你是不清楚的,这就是封装。
5.类的作用域
类定义了一个新的作用域 ,类的所有成员都在类的作用域中 。 在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
就如同我们上面将类里较长的方法写在外面一样,需要::指明 。
6.类的实例化
用类类型创建对象的过程,称为类的实例化
我们先看一个例子:
class S
{
public:
int getNum_a()
{
return _a;
}
int setNum_a(int a)
{
_a = a;
}
private:
int _a;
int _b;
};
int main()
{
//可以这样使用吗?
S.getNum_a();
return 0;
}
答案是不可以的。
如果这样使,那么一个类岂不是只能使用一次?再想用还得再写一个类?
1. 类是对对象进行描述的 ,是一个 模型 一样的东西,限定了类有哪些成员,定义出一个类 并没 有分配实际的内存空间 来存储它
也就是说,我们仅仅类进行了声明,就像我们C语言里使用的函数,我们有声明,有定义,但是在不调用这个函数时,编译器不会为他开辟栈帧,也就是空间,我们的类在没有实例化出对象时编译器也是不会分配空间给类的。
int main()
{
S a;
a.setNum_a(6);
cout << a.getNum_a() << endl;
return 0;
}
所以这样实例化对象,我们的对象就是a,类型为S这个类,所以我们的a就有了空间大小,可以调用a里的各种方法。
2. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
int main()
{
S a;
S b;
S c;
return 0;
}
这样是可以的。
但是由此我们也引出了一个问题,那就是每一个对象里的方法都要开空间吗,这显然不是,就像一个小区,人就是对象,篮球场就是方法,房间就是成员变量,房间不可以共享,但是篮球场是可以的,而且用的是同一个篮球场。
也就是说,有一个公共区来存放我们类里的方法,方法并不在对象里存储,我们可以验证一下。
这就是事实,只计算了private里的成员变量,大小为8个字节,计算规则与C语言里计算结构体大小的方法是一样的,可以参考:自定义类型详解。
7.类的对象大小的计算
参考:自定义类型详解
但是有一点不同的是空类的大小。
测试空类大小
#include <iostream>
using namespace std;
class mid
{
public :
void COUT()
{
cout << "cout" << endl;
}
private:
int a;
int b;
int c;
};
class null
{
};
int main()
{
mid a;
cout << sizeof(a) << endl;
null b;
cout << sizeof(b) << endl;
return 0;
空类实例化出的对象大小不为0。
8.类成员函数的this指针
#include <iostream>
using namespace std;
class Stack
{
public :
void push(int x)
{
cout << "push:x" << endl;
}
void pop()
{
cout << "pop" << endl;
}
//构造函数和析构函数要公有
Stack()
{
int _capacity = 3;
int* _a = (int*)malloc(sizeof(int) * _capacity);
}
//这里有一个隐含的指针Stack *this
//但是在实参和形参处不可以显式写出来,函数里可以。
Stack(int capacity)
{
//_capacity = capacity;
// int* _a = (int*)malloc(sizeof(int) * _capacity);
//在这里实际上是这样的
this->_capacity = capacity;
int* a = (int*)malloc(sizeof(int) * this->_capacity);
}
~Stack()
{
free(_a);
_top = 0;
_capacity = 0;
}
private:
int* _a;
int _top = 0;
int _capacity;
};
int main()
{
Stack c;
//这里编译器会替我们做处理
//隐含地传了&c,不可显式写出。
c.push(1);
c.pop();
return 0;
}