面向过程:把问题分解成若干步骤,把每个步骤封装成函数,然后依次调用各个函数
- 面向对象:把问题看作对象之间的相互作用
- 如何把一头大象放进冰箱
- 创建大象和冰箱
- 冰箱自动打开
- 大象能进入冰箱
- 冰箱自动关门
- 面型对象编程中,一切都是类和对象
类——类型 一类事物的统称,泛化的抽象概念
- 如:我想买一台电脑 ——电脑 -> 类
对象——变量 ;类实例化的结果,特指一个原本存在的事物
- 如:我正使用的电脑坏了 ——电脑->对象
用类定义对象的过程,称为实例化
-
C++中定义类可以使用struct 也可以是class
class 类名{ //访问控制属性: //属性 //方法 }; //实例 class Complex{ public: //属性 特征 double r;//实部 double v;//虚部 public: //方法 void show(void){ cout << r << "+" << v << "i" <//实例化 Complex *ss = new Complex; }
-
封装
把一类事物都拥有的特征抽象为属性,把一类事物都具有的行为抽象为方法, 并加以访问可控制的属性限制,我们称之为封装
-
访问控制属性
-
public 公开的
-
protected 受保护的
-
private 私有的
-
一般情况下,属性会设置为受保护的或私有 的,方法会设置公开的,也会为私有属性提供公开的set和get
-
访问控制属性 类中 类外 子类 public 可以访问 可以访问 可以访问 pretected 可以访问 不能访问 可以访问 private 可以访问 不能访问 不能访问
-
-
继承
-
多态
class 类型名{
//属性
//方法 可以直接访问成员属性
};
实例化对象
-
用类型定义变量
-
类名 对象名 实例化的对象若不经过初始化,则是垃圾值,需要额外的赋值语句
-
如果需要在构造对象时,给对象进行初始化,需要在定义类时,添加构造函数
-
实例化对象一定要调用构造函数,如果一个类没有构造函数,则编译器会自动安排一个无参数的构造函数
-
//无参构造实例化对象 在栈内存实例化对象 ObjType obj; //调用无参构造实例化对象 ObjType obj(); //函数声明 声明一个函数名为obj 返回值类型为ObjType,且没有任何参数 ***!!! ObjType obj(arg,..); //调用有参构造函数
//C++11
ObjType obj{}; //调用无参构造
ObjType Obj{arg,…}; //调用有参构造
//在堆内存中实例化对象
ObjType *po = new ObjType; //调用无参构造
ObjType *po = new ObjType(); //调用无参构造
ObjType *po = new ObjType{}; //调用无参构造
ObjType *po = new ObjType(arg,...);
ObjType *po = new ObjType{arg,...};
//匿名对象 没有变量(对象)名的对象 ObjType(); //调用无参构造函数实例化一个匿名对象 ObjType(arg,...); ```
在实例化对象时调用的函数,称为构造函数
class 类{
public:
//构造函数
类名(形参列表){
}
};
-
构造函数没有返回值,也不能声名为void
-
构造函数的函数名和类型名是一样的
-
如果一个类没有实现构造函数,则编译器会自动生成一个无参的构造函数
-
如果一个类中有提供构造函数,则编译器不会为其提供默认无参构造
Complex obj;//无参 Complex obj();//声名函数,返回值类型为Complex, 没有任何参数 Complex obj{};//无参 Complex obj{123};//有参 -
构造函数允许重载
重载构造函数,可以不同的需求来实例化对象
-
构造函数的任务基本上是对属性初始化
通过类实例化对象,一定需要匹配的构造函数,否则就会编译报错
class rectangle{
public:
char c;
int lonth;
int wide;
public:
rectangle(char _c, int _lonth, int _wide){
c = _c;
lonth = _lonth;
wide = _wide;
}
public:
void make_it()
{
for(int i = 0; i < lonth ;i++){
for(int j = 0; j < wide; j++){
if(j != 0 || j != wide-1){
if(i==0 || i == lonth){
cout<
return lonth * wide;
}
void show()
{
cout<<"lonth:"<
struct和class在C++中的区别
- class访问控制属性是private, struct是public
友元
可以声明全局函数有友元函数,那么在友元函数中可以访问该类的私有/保护成员
class 类名{
friend 友元函数声名
};
-
可以声明类为友元类,那么在友元类中可以访问该类的私有/保护成员
- 友元类的声明是单面的
-
友元其实是打破了封装的效果,但某些情况不得已而为之
this
本质是一个指针常量
- 当成员属性名和成员函数中的局部变量同名时,遵循局部变量优先原则,但是我们可以用this->成员名的方法访问类成员属性
- 在所有的成员函数中都隐含一个this指针,指向正调用这个函数的对象,但是类中没有冲突的情况下可以省略
- obj.func(); 底层实现会转换为 func(&obj); func(Type *const this);
构造函数初始化列表
只有沟槽函数才有初始化列表
class 类名{
类名(形参列表):初始化列表{
}//像这样:stu(int id, string name, int age):id(id), name(name), age(age)
}
只有在初始化列表中才成为初始化,在构造函数内部则称为赋值,如果有const修饰的或者为引用的成员,则必须用初始化列表进行初始化
class A{
private:
cosnt int a;//常属性成员
int &b;//引用
public:
A(int a,int &b):a(a), b(b){}
}
-
class B{
private:
int n;
int m;
public:
B(int a=1024):m(a),n(m){
}
int getN(){
return n;
}
int getM(){
return m;
}
};
B b;
cout << b.getN() << ":" << b.getM() << endl;
-
初始化列表执行顺序与初始化列表的书写顺序无关,只与定义顺序相关
组合
关于成员有类类型时,在构造函数的初始化列表中,默认调通类类型成员的无参构造进行初始化,也可以在成员中传递构造函数的参数
class Point{
double x;
double y;
public:
Point(double x,double y):x(x),y(y){
}
};
class Circle{
Point center;
double r;
public:
//center(0,0)即调用 Point(double x,double y)有参构造
Circle():center(0,0){
}
};
int main(){
Circle c; //编译报错 解决方案有2: 1.让Point提供无参构造 2.在Circle构造函数的初始化列表中显示调用Point的有参构造
return 0;
}
构造函数的初始化列表中,如果有必要调用有参构造函数进行构造成员,需要显式调用
构造函数的执行顺序:先按照类类型的成员的定义顺序依次调用构造函数,然后执行构造函数体
可以利用构造函数和析构函数,在main函数之前、之后进行输出
class A{
public:
A(){
cout << "A()" << endl;
}
~A(){
cout << "~A()" << endl;
}
};
A obj; //在main begin之前输出 A() 在main end之后输出 ~A()
int main(){
cout << "main begin" << endl;
cout << "main end" << endl;
return 0;
}
代理构造(消失了?!
- 在一个类的初始化列表中,调用另一个类的构造函数
单参构造
- 如果一个类A提供了一个T类型的单参构造函数,那么T类型的数据可以隐式调用该单参构造函数转换为A类型的对象
- 如果需要禁止T类型数据隐式调用单参构造函数转换为A类型的对象,可以在A类型的单参构造函数前加上explicit关键字
- 加上explicit之后就不能隐式进行,只能显示调用构造函数进行转换
静态属性
属性用static修饰,称为静态属性
-
静态属性需要在类外进行定义
-
class 类名{
public:
static int s;//声明 没有分配内存空间
};
int 类名::s = 1024; //在类外定义和初始化
-
静态属性存储于全局数据区
-
成员属性一般存在在栈区或者堆区
静态属性属于类而不是某个对象,每个对象都能访问,访问到的都是同一个
静态属性相当于加了 类名: 作用域限定的全局变量
静态属性可以直接用类名:静态属性的方式直接访问
也可以用 对象:静态属性 的方式进行访问
静态方法
方法用static修饰,称为静态方法
静态方法属于类而不是某个对象
- 相当于加了 类名:作用域限定符 的全局函数
- 可以直接使用 类型:静态方法名(参数列表)直接调用静态方法
- 也可以使用 对象:静态方法名(阐述)直接调用
- 静态方法中不能使用this
- 静态方法中不能直接访问成员变量,不能直接调用成员函数
- 静态方法中可以直接访问静态属性和直接调用其他静态方法
常对象和常函数
-
C++中const和C语言中的const是不一样的
- C语言中的const仅仅是表示只读
- C++const表示却是常量 g++编译器会对const进行替换处理,在编译时对使用const定义变量进行直接替换,好比C语言中的宏定义 C++中const变量必须初始化
-
常对象,const修饰的对象 常对象的属性是只读(read only)
-
mutable修饰属性,表示常对象也可以修改该属性的值
-
常方法/常函数(一定得是成员函数才行)
class A{
//const 常方法、常函数 const修饰的是 *this
返回值类型 成员方法名(形参列表)const{
}
};
-
常对象,只能调用常方法 不能调用非常函数
-
普通对象能够调用常方法 如果没有非常方法的情况下
-
常函数和非常函数构成重载
- 常对象只能调用常方法
- 普通对象在有非常方法时,选择调用非常方法,如果没有非常方法,则调用常方法
-
在实践过程中,如果一个方法的常方法和非常方法的实现是一样,且不会修改成员时,都会把方法定义成常方法
析构函数
在对象的生命周期结束时会自动调用析构函数
class A{
public:
A(){}//构造函数
~A(){}//析构函数
}
析构函数没有返回值,也不能是void
析构函数的名字:~类名
析构函数没有参数,不能重载
如果一个类没有实现析构函数,则编译器会自动生成一个析构函数,该析构函数为一个空析构
析构函数不能且无法手动调用,只能在对象消亡时自动调用
- 栈内存中的对象在栈内存回收时会调用析构
- 堆内存——>delete
- 全局变量——>程序结束
社么时候申请析构函数
- 打开文件/申请动态内存——在析构函数中释放动态内存或关闭文件
- 避免内存泄漏
析构函数的执行顺序:
- 与构造函数相反,
成员属性指针和成员方法指针
成员属性指针:
- 定义:成员类型 类名::*指针名;
- 初始化:类名::*指针名 = &类名::属性名;
- 成员属性指针保存的并不是真实的内存地址
- 解引用:对象。*成员属性指针
- 间接解引用 对象指针->*成员属性指针
成员类型 类名::*指针名;
成员类型 类名::*指针名 = &类名::属性名;
指针名 = &类名::属性名;
成员属性指针保存的并不是真实的内存地址 (1)
--直接成员指针解引用 对象
对象.*成员属性指针
--间接成员指针解引用 对象的指针
对象指针->*成员属性指针
成员方法指针:
//成员方法指针定义
成员函数返回值类型 (类名::*pf)(形参列表);
成员函数返回值类型 (类名::*pf)(形参列表) = &类名::方法名;
pf = &类名::方法名;
(对象.*pf)(实参列表);
(对象指针->*pf)(实参列表);



