1.面向过程和面向对象初步认识
在C语言中,除了库中的函数,你会发现我们想实现些什么都要自己写,而且还有很多不方便的地方,这样那样的缺陷和不足,相信很多人都比较难受,我们C++的祖师爷他也难受,所以才开创了C++。
C语言就是一门面向过程的语言,什么叫面向过程,就是注重过程,好比洗衣服,整个流程自己弄,相当于让你写篇小作文,详细写你是如何洗衣服的。而面向对象,就是你妈妈让你洗衣服,问你洗完衣服了吗,你说我用洗衣机洗完了。
总结:
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完
成。
2.类的引入
C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。比如:
之前在数据结构初阶中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现,
会发现struct中也可以定义函数
#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就可以。
【访问限定符说明】
1. public修饰的成员在类外可以直接被访问
2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. 如果后面没有访问限定符,作用域就到 } 即类结束。
5. class的默认访问权限为private,struct为public(因为struct要兼容C)
问题:C++中struct和class的区别是什么?
解答:C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来
定义类。和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类
默认访问权限是private。注意:在继承和模板参数列表位置,struct和class也有区别,后序给大
家介绍
封装:
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来 和对象进行交互。
也就是说可以使用类里公开的方法实现你想实现的,但是如何实现你不知道,也无法直接修改和查看类里私有的成员变量。
好比一个计算机,你可以使用计算机算数,但是他如何实现,以及工作原理你是不清楚的,这就是封装。
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; }