面向过程编程:
关注是问题解决的过程步骤,算法
面向对象编程:
关注的是谁能解决问题(类),需要什么样的数据(成员变量),具备什么样的技能(成员函数)才能解决问题。
抽象:找出一个能够解决问题的“对象”(观察研究对象),找出解决所必须的数据(属性)、功能(成员函数)。
封装:把抽象的结果,归结为一个类(数据类型),然后实例化出类对象,设置对象的属性,调用对象的功能达到解决问题的目的。
继承:在解决问题前,先寻找之前的类能不能解决问题,或解决部分问题,如果可以则把旧的类继承后再次拓展,来缩短解决问题的时间,降低
解决问题的难度。
多态:对象的多种形态,外部看到一个对象发出指令,对象会根据自身情况做出独特的反应。
一、类和对象
1、通过分析对象的属性和行为设计出一个类。
2、类就是数据类型
简单类型:只能表示一个属性(变量),C/C++内建数据类型
数组类型:可以表示多个属性(变量),类型必须相同。
结构类型:可以表示多个属性(变量),但缺少行为(函数)。
类类型:即能表示属性,也能表示行为,一直复合数据类型。
3、对象就是类这种数据类型创建出的实例,相当于结构变量。
class Student
{
属性(成员变量);
行为(成员函数);
};
Student stu;
二、类的定义与实例化
1、类的一般形式
class 类名 : 继承方式 父类
{
public/private/protected: // 访问控制限制符
成员变量;
// 构造函数
类名(形参表)
{
}
// 析构函数
~类名(void)
{
}
};
2、类的访问控制限定符
public:公有成员,在任何位置都可以访问
private:私有成员,只能在类(自己)的成员函数中访问
protected:受保护成员,只能在类(自己)和子类中访问
注意:类中的成员变量、成员函数默认是 private,结构中的成员和成员函数默认是 public。
注意:C++中类和结构的区别只有成员函数和成员变量的默认访问权限不同。
3、构造函数
1)什么是构造函数:类的同名函数就是构造函数,没有返回值。
2)什么时候调用,谁调用,调用几次?
创建类对象时会被自动调用(每创建一个类对象,就会调用一次),对象整个生命周期中一定会被调用一次,只能被调用一次。
3)负责干什么
成员变量的初始化,分配相关资源,设置对象的初始状态。
class 类名 : 继承方式 父类
{
// 构造函数
类名(形参表)
{
}
};
4、类型的创建过程
1.分配类型所需要空间,无论栈还是堆。
2.传递实参调用构造函数,完成如下任务:
1)根据继承表依次调用父类的构造函数
2)根据成员变量的顺序依次调用成员变量的构造函数。
3)执行构造函数体中的代码。
注意:执行构造函数的代码是整个构造函数的最后一步。
要保证构造函数代码所需要的一切资源和先决条件在该代码执行前已经准备充分,并得到正确的初始化。
5、对象的创建方法
1.在栈上创建:类名 对象;// 不需要括号
类名 对象(实参);
2.在堆上创建:类名* 对象指针 = new 类名;
类名* 对象指针 = new 类名(实参);
3.创建多个对象:
类名 对象 = {类名(实参),类名(实参),类名(实参)};
类名* 对象指针 = new 类名[n]{类名(实参),类名(实参)};
注意:通过malloc创建的类对象不能调用构造函数。
注意:通过new[]创建的对象,一定要通过delete[]释放。
6、类的声明、实现、调用
1.在头文件中声明
class 类名 : 继承方式 父类
{
成员变量;
public: // 访问控制限制符
// 构造函数
类名(形参表);
// 析构函数
~类名(void);
// 其他成员函数
返回值 函数名(参数列表);
};
2.源文件实现类的相关函数
返回值 类名::函数名(参数列表)
{
}
3.调用时只需要导入头文件,然后与类函数所在的源文件一起编译即可。
注意:如果一个类内容不多,可以考虑在头文件中完全实现。
也可以只在头文件中实现一些简单的成员函数。
注意:类中自动生成的函数,在源文件中实现时,也需要在头文件中声明。
class和struct的区别?
class的默认继承和访问权限是private,struct的默认继承和访问权限是public。class能做模板的参数,struct不行。
三、构造函数与初始化列表
1、构造函数可以被重载(同一个名字的函数有多个不同版本)
2、缺省构造是编译器自动生成的一个什么都不做的构造函数(唯一的作用就是避免编译错误)。
注意:当类实现一个有参构造时,缺省构造就不会再自动生成,如果有需要必须显示地写出来。
3、无参构造未必无参,当给有参构造的所有参数设置默认形参,调用这种构造函数就不需要传参。
注意:所谓的“编译器生成的某某函数”其实不是真正语法意义上的函数,而是功能意义上的函数,编译器作为可执行指令的生成者,它会直接生成具有某项功能的二进制指令,不需要借助高级语言语义上的函数完成此任务。
注意:如果一个类是其他类的成员变量,那么一定要保证它有一个无参构造,当B的构造函数执行时会执行成员变量的无参构造,而此时类B是无法给类A成员变量提供参数的。
4、单参构造与类型转换
如果构造函数的参数只有一个,那么Test t = n语句就不会出错,它会自动调用单参构造来达到类型转换的效果。
如果想禁止这种类型转换需要在单参构造前加 explicit
5、初始化列表
为类成员进行初始化用的。
构造函数(参数):成员1(参数1),成员2(参数2)...
const int num;
Test(int n):num(n)
{
}
通过初始化列表可以给类成员变量传递参数,以此调用类成员的有参构造。
初始化列表也可以给 const 成员、引用成员进行初始化。
#include <iostream>
using namespace std;
class A
{
public:
int num;
A(int _num)
{
num = _num;
cout<<"我A的有参构造"<<endl;
}
};
class Test
{
public:
string str;
const int num;
int& xiu;
A a;
Test(int num,const char* str,int& x):num(num),str(str),a(num),xiu(x)
{
cout<<"---"<<endl;
}
};
int main()
{
int x = 100;
Test t(10,"aa",x);
//Test t = {10};
//t.num = 100;
cout<< t.num << endl;
cout<<t.xiu<<endl;
x = 1000;
cout<<t.xiu<<endl;
}
成员的初始化顺序与初始化列表没有关系,而是在类中的定义顺序有关。
#include <iostream>
using namespace std;
class A
{
public:
A(int n)
{
cout<<"A"<<endl;
}
};
class B
{
public:
B(int n)
{
cout<<"B"<<endl;
}
};
class C
{
public:
C(int n)
{
cout<<"C"<<endl;
}
};
class Test
{
public:
A a;
C c;
B b;
//A a;
Test(int c1,int a1,int b1):c(c1),a(a1),b(b1)
{
}
};
int main()
{
// A a1;
// B b1;
// C c1;
Test t(1,2,3);
}
注意:初始化列表运行类成员变量还没有定义成功。