C++ 之 面向对象基础知识
面向过程设计
实际上就是模块化编程,简单来说就是程序 = 数据结构 + 函数
,但是函数和变量是分开的也就是说property和behavior是分割的。
graph LR;
A((结构化程序设计)) --> B((模块化编程))
面向对象设计
graph LR;
A[一类事物] -->|抽象| B[共同属性 ]
B --> C[数据结构]
A -->|抽象| D[行为/操作]
D --> E[函数]
C -->|封装| F[类]
E -->|封装| F[类]
类的定义
class 类名
{
访问范围说明符:
成员变量1
成员变量2
···
成员函数声明1
成员函数声明2
访问范围说明符:
更多成员变量1
更多成员变量2
···
更多成员函数声明1
更多成员函数声明2
};
复制构造函数
class X{};
X::X(X &);
X::X(const X &);
X::X(X); //该构造函数形式不允许存在
作用:
- 当用一个对象去初始化同类的另一个对象时。
- 如果某函数有一个参数是类A的对象,那么该函数被调用时,类A的复制构造函数将被调用。
- 如果函数的返回值是类A的对象时,则函数返回时,A的复制构造函数被调用
类型转换构造函数
目的:
- 实现类型的自动转换
- 特点
- 只有一个参数
- 不是复制构造函数
- 编译系统会自动调用
转换构造函数
建立一个临时对象/临时变量
Exsample :
class Complex{
public:
double real, imag;
Complex(int i){
cout << "IntConstructor called" <<endl;
real = i;
imag = 0;
}
Complex(double r,double i) {
real = r;
imag = i;
}
};
int main(){
Complex c1(7,8);
Complex c2=12;
c1=9;//9被自动转换成一个临时Complex对象
cout<< c1.real<<","<<c1.imag <<endl;
return 0;
}
静态成员变量与函数
- 普通成员变量每个对象有各自的一份,而静态成员变量一共就一份,为所有对象共享。
- 普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象。
sizeof 不计算静态成员变量, 静态函数不可以访问非静态成员变量。
访问方法:
- 类名::成员名
CRectangle::PrintTotal();
- 对象名.成员名
CRectangle r; r.PrintTotal();
- 指针->成员名
CRectangle*p=&r; p->PrintTotal();
- 引用.成员名
CRectangle&ref=r; int n=ref.nTotalNumber;
友元
一个类的友元函数可以访问该类的私有成员, 但是友元类之间的关系,不能传递,不能继承
实现方法:
- 将一个类的成员函数(包括构造,析构函数)定义为另一个类的友元函数
class CCar;//提前声明CCar类,以便后面CDriver类使用
class CDriver{
pubic:
void ModifyCar(CCar*pCar);//改装汽车
};
class CCar{
private:
int price;
friend int MostExpensiveCar(CCar carsl,int total);//声明友元
friend void CDriver:ModifyCar(CCar*pCar); //声明友元
};
- 将一个类定义为另一个类的友元类
class CCar{
private:
int price;
friend class CDrpver/声明CDriver为友元类
};
class CDriver{
public:
CCar myCar;
void ModifyCar(){//改装汽车
myCar.price +=1000; //CDriver是CCar的友元类可以访问其私有成员
}
};
静态成员变量&函数
注意静态变量&函数可以直接通过类进行调用,同时静态成员变量需要在类外*.cpp文件中进行初始化
头文件中
class chart{
public:
static int chartCount;
static initChartCount(){
chartCount = 0;
}
};
源文件中
int chart::chartCount = 0;
void test(){
chart::initChartCount();
}
成员对象和封闭类
成员对象:一个类的成员变量是另一个类的对象
封闭类(Enclosing): 包含成员对象的类
当封闭类对象生成时
- 执行所有成员对象的构造函数
- 执行封闭类的构造函数
成员对象的构造函数调用顺序
- 和成员对象在类中的说明(定义)顺序一致
- 与在成员初始化列表中出现的顺序无关
当封闭类的对象消亡时
- 先执行封闭类的析构函数
- 执行成员对象的析构函数
构造函数和析构函数的调用顺序相反
派生类
class 派生类 : public 基类{}
构造函数以及析构函数调用顺序
创建派生类的对象时,执行派生类的构造函数之前:
- 调用基类的构造函数, 初始化派生类对象中从基类继承的成员
- 调用成员对象类的构造函数
初始化派生类对象中成员对象执行完派生类的析构函数后:
- 调用成员对象类的析构函数
- 调用基类的析构函数
析构函数的调用顺序与构造函数的调用顺序相反
public继承的赋值兼容规则
class base{};
class derived:public base{};
base b;
derived d;
- 派生类的对象可以赋值给基类对象
b=d
- 派生类对象可以初始化基类引用
base&br=d
- 派生类对象的地址可以赋值给基类指针
base*pb=&d
访问范围说明符
基类的private成员:可以被下列函数访问
- 基类的成员函数
- 基类的友员函数
基类的public成员:可以被下列函数访问
- 基类的成员函数
- 基类的友员函数
- 派生类的成员函数
- 派生类的友员函数
- 其他的函数
基类的protected成员:可以被下列函数访问
- 基类的成员函数
- 基类的友员函数
- 派生类的成员函数可以访问当前对象的基类的保护成员
注意:
- 如果在派生类中有基类一样的变量或函数,则使用域运算符进行区分
继承关系和复合关系
继承:“是”关系。
class CCircle : public CPoint {
double r;
}
- 基类A,B是基类A的派生类。
- 逻辑上要求:“一个B对象也是一个A对象”。
复合:“有”关系。
class CCircle {
double r;
CPoint center;
}
- 类C中“有”成员变量k,k是类D的对象,则C和D是复合关系
- 一般逻辑上要求:“D对象是C对象的固有属性或组成部分”。
复合关系的使用
正确写法:
- 为“狗”类设一个“业主”类的对象指针;
- 为“业主”类设一个“狗”类的对象指针数组。
class CMaster;//CMaster必须提前声明,不能先写CMaster类后写Cdog类
class CDog{
CMaster * pm;
};
class CMaster{
std::vector<CDog *> dogs
}
任世事无常,勿忘初心