类是定义同一类所有对象的变量和方法的模型,是struct的延伸与拓展。类可以定义类变量和类方法。系统在第一次在程序中遇到一个类时为这个类建立它的所有类变量的拷贝,这个类的所有实例共享它的类变量。
对象的定义:
class类型一旦被定义,它的实例变量(对象)就能被创建,并初始化,且能定义指针变量指向它。实例化的类就是对象。
在C++里,struct 和 class其实本质上是一样的,唯一区别就是struct默认是公开的,class默认是私有的,公开时需要声明为public;还有就是C语言里的结构体,不能包含函数和继承,C++可以包含函数以及继承其他的类。
面向对象编程主要特点: 封装、多态、抽象、继承
封装:
就是将实现的细节隐藏,而暴露公有接口;通常是将所有的成员变量私有化;使程序更有可重用性和可维护性;
封装之后,class内有三种修饰符,public、private、protected。
1、public修饰的成员变量
在程序的任何地方都可以被访问,就是公共变量的意思,不需要通过成员函数就可以由类的实例直接访问
2、private修饰的成员变量
只有类内可直接访问,私有的,类的实例要通过成员函数才可以访问,这个可以起到信息隐藏
3、protected是受保护变量
类内和子类可直接访问,也就是说,基类中有protected成员,子类继承于基类,那么也可以访问基类的protected成员,要是基类是private成员,则对于子类也是隐藏的,不可访问
友元函数与友元类——可以直接访问私有成员变量
class Test{ public: Test(); int exam(); //不需要通过成员函数就可以由类的实例直接访问 protected: int pro_a; //类内和子类可直接访问 float pro_b; private: int pri_c; //只有类内可直接访问,私有的 double pri_d; friend void friend_fun0(Test &test); //友元函数 friend class Test01; //友元类 }; void friend_fun0(Test &test) { cout << test.pro_a << endl; } class Test01{ public: void get_test(Test &test){ cout << test.pro_a << endl; }; };
构造函数与析构函数——类分配与回收
构造函数在对象初始化的时候执行,析构函数在对象离开作用域时执行。构造函数支持重载操作,看一下构造函数与析构函数执行:
class Test { public: Test(){cout << "构造函数Test" << endl;} ~Test(){cout << "析构函数Test" << endl;} }; class Test01 { public: Test01(){cout << "构造函数Test01" << endl;} ~Test01(){ cout << "析构函数Test01" << endl;} private: Test test; }; void main() { /*初始化Test01,先执行Test的构造函数,再执行Test01的构造函数*/ cout << "a" << endl; Test01 a; /*b和c属于copy赋值,不执行构造函数*/ cout << "b" << endl; Test01 b(a); cout << "c" << endl; Test01 c = a; /*new分配,只有delete才会执行析构函数*/ cout << "d" << endl; Test01 *d = new Test01(); delete d; cout << "delete d" << endl; }1、 构造函数在对象初始化结束后执行。例如 Test01 a;
输出:
构造函数Test
构造函数Test01
2、 Test01 b(a) 与 Test01 c=a 是把a复制到b和c中,不执行构造函数,因为没有初始化操作
3、 使用new是在堆内分配初始化,需要手动回收。例如,把 delete d删掉,则*d不会被回收,其析构函数不会被执行。而其他的对象是在栈内被分配初始化,因此在main执行完之后会被回收。
4、不要在构造函数中抛出异常,因为异常会导致析构函数不会被执行(构造函数异常表示创建对象不完整);析构函数也不应该抛出异常,
多态与抽象:
多态的使用离不开虚函数。虚函数是为了子类继承父类并希望实现与父类同名的方法而出现的。是一种类方法的抽象。
1.0 虚函数
class Test
class Test_child:public Test
当执行下面的语句
Test *a = new Test_child(); a->fun();
在编译阶段,没有任何对象(对象是执行阶段创建的),因此a->fun();在编译的时候是无法确认执行的父类还是子类的fun函数。此时,如果把fun定义为virtual类型,则编译器会把a->fun();编译为 (a->vtpl[1])(a) <vtpl为虚表,1代表在虚表的位置>,此时在执行的时候根据虚表的创建来实现执行子类的fun().
1.1 纯虚函数
纯虚函数就是基类只定义了函数体,没有实现过程。 virtual void fun() = 0;虚函数在子类可以不进行重载,但是纯虚函数在子类必须重载。
虚函数实现了面向对象的思想,但是降低了执行效率。
2、重载
重载就是函数重写,函数名相同,函数参数的变量属性或者参数数量不同。
运算符重载: 通过operator与封装实现类的+ - * / ++ 等操作,具体逻辑在operator修饰的函数内实现
多态的实现包含上面两种———— 虚函数(纯虚函数) 与 重载
继承:
子类继承父类,继承父类public、protected属性与方法,可添加属于自己的方法,或者重写父类虚函数方法。子类继承分为三种:public private protected
1、public继承不改变基类成员的访问权限
2、private继承使得基类所有成员在子类中的访问权限变为private
3、protected继承将基类中public成员变为子类的protected成员,其它成员的访问 权限不变。
4、基类中的private成员不受继承方式的影响,子类永远无权访问。