《C/C++学习指南》语法篇—笔记 (十七、面向对象编程 类)
- 类(class)
- 类的成员
- This指针
- 重命名问题与名命规范
- 类的封装
- class的分离式写法
- 类的构造函数
- 类的析构函数
- 默认构造函数
- 成员的初始化与析构
面向对象编程,一种设计思想。
面向对象编程的实现:
- 假设存在一个对象,初步设想它应该提供哪些服务。
- 定义如何使用它的服务,细化为函数。创建销毁对象,其它功能函数(该对象提供的服务)
- 选择一种实现方法,写程序完成上述函数接口。
类(class)
是对结构体struct的增强,也是用于自定义类型。将关键字struct改为class;增加访问修饰符 pubilc/private/protected
class Object
{
public:int a; //a,b成员变量double b;
};
访问成员变量的方法,与struct完全相同
Object Obj;
obj.a = 11;
obj.b = 12.0;Object* p = &Obj;
p->a = 12;
p->b = 13.0;
访问修饰符:public,private,propected
用于表示类的成员函数是否允许被外部访问。
- pubilc :公开的,所列的成员可被外部访问
- private :私有的,所列的成员不可以被外部访问
- protected :受保护的,不可以被外部访问,可以被继承
访问修饰符后面加:
,一般顶格书写,不缩进,每个一行,每个成员变量只受前一个修饰符的限制。如果没有访问修饰符,默认是private的。
类的成员
成员变量和成员函数。在类体中,成员函数和变量的排列顺序的自由的。
定义在class内的函数,称为该类的成员函数。成员函数的访问,也是用.
或->
,成员函数同成员变量一样受访问修饰符的限制。
class Object
{
public:int a;void Test(){printf("hello! \n");}
};Object Obj;
obj.Test();
Object* p = &Obj;
p->Test();
This指针
在类里打印出a:
class Object
{
public:int a;void Test(Object* that){//打印aprintf("a=%d \n",that->a);}
};
Object Obj;
Obj.Test(&Obj);
使用this指针方法简化:当Test被调用时,已经把对象Obj的指针传给它了。使用this指针,可以直接访问本类的其他成员(变量,函数),不受public/private的限制。this就是本对象的地址,在形式上做了一个简化,使得用户少传一个对象指针作为参数。
在类的函数里面,this可以省略,编译器会自己添加this->
class Object
{
public:int a;
private:int b; //b是private的,但this->可以访问
public:void Test(){//打印aprintf("a=%d,b=%d \n",this->a,this->b);printf("a=%d,b=%d \n",a,b); //省略this->}
};
Object Obj;
Obj.Test();
重命名问题与名命规范
就近原则:
- 在成员函数里,当局部变量与成员变量重名时,该变量指的是局部变量。如果要指定成员变量,则必须加上
this->
- 在成员函数里,当成员变量与全局变量重名时,该变量指向的是成员变量。如果要指定全局变量,则必须加上
::
- 在成员函数与全局函数重名时,默认指成员函数。
::
全局符号this->
成员
名命规范:
- 类名:每个单词的首字母大写,一般用名词形式
- 成员函数:每个单词首字母大写,一般用动词形式
- 成员变量:小写开头,第二个单词开头大写,通常加上前缀
m_number
类的封装
封装:把细节隐藏在内部,只把函数接口暴露在外。在C/struct中,由于struct内部是可以自由访问,所以无法实现封装(用户可能不小心破坏内部数据),在引入class和访问修饰符后,就可以实现完全的封装。
- 把所有成员变量设为private
- 添加函数接口,供外部操作该对象,及其他功能接口
成员变量:
- 完全公开:用public修饰符
- 完全不允许访问:用private
- 只读:getter,写个pubilc访问该成员变量的函数
- 只写:setter,写个public设置该成员变量的函数
class的分离式写法
把class的成员函数定义在class之外,即class的大括号外面。
- 成员变量:写在类里面
- 成员函数:在类里保留其函数声明,而函数的定义写在类体之外
- 函数定义写在外面的时候,要加上类名限定(
Object::
),::
理解为表示范围的符号。
分开为头文件 Object.h
class Object
{
public:int x;void Test();
};
源文件 Object.cpp
#include <stdio.h>
#include "Object.h"void Object::Test()
{printf("x is %d \n",x); //仍然可省略this->
}
类的构造函数
一种特殊的成员函数:
- 函数名与类名必须相同
- 没有返回值
- 构造函数可以带参数,也可以重载!!!
class Cricle
{
...
public:Cricle(){x=y=0;radius = 1;}Cricle(int x, int y, int r){this->x = x; //这里要加this了吧~this->y = y;this->radius = r;}
};
构造函数如何调用:
构造函数和普通函数不一样,一般不显式调用,在创建一个对象时,构造函数被自动调用,由编译器完成。
Cricle a; //调用第一个不带参数的构造函数
Cricle a(1,1,4); //调用后一个
构造函数作用:对象的初始化动作。
例如下面基本类型的初始化:
int a(10); // 和 int a = 10; 一样
Struct s= {1,"aa"}; //某个结构体的初始化
类的析构函数
- 名称固定:类名前加
~
- 没有返回值
- 不能带参数,只有一个,不允许重载
class Object
{
...
public:~Object(){}
};
调用:
构造函数:对象被创建时被调用
析构函数:对象被销毁时被调用(不显式调用,被编译器自动调用),对于局部变量(对象),在超出变量作用域(某大括号)后,该对象失效,自动调用析构。
作用:
对象在销毁之前,做一个清理善后工作,例如释放申请的内存,关闭打开的文件。
.h/.cpp分离写法
class DataStore
{
public:DataStore(); //函数声明~DataStore();
};DataStore::DataStore(){}
DataStore::~DataStore(){}
默认构造函数
把不需要传参的构造函数,或所有参数都有默认缺省值,称为默认构造函数。
Object();
Object(int a=10, int b=11);
默认构造函数是重要的,在构造数组对象的时候,没有默认构造函数则无法构造。
如果一个类没有写任何构造函数,则编译器隐含的为其添加一个构造/析构函数。只有当没写的时候才给添加,一旦写了一个,就不给添加了。
Object objs[4];
成员的初始化与析构
成员变量本身也是class类型时:
- 构造:成员被依次构造:从前到后;先执行成员的构造函数,再执行自己的构造函数
- 析构:成员为依次析构:从后到前;先执行自己的析构函数,再执行成员的析构。
初始化列表!!!
1、基本类型的初始化:
class Object{
public:Object():x(1),y(2){}
};
2、class类型的初始化:
//初始化(和赋值功能上一样,性能上有时节省CPU操作)
class Object{
public:Object():m_chlid(1,2){}
};
//赋值
class Object{
public:Object() //已经对m_child进行了构造{m_child.x = 1;m_child.y = 2; //又多了一步操作}
};