1. 规范命名
在使用类(class)写程序时有以下规范
成员变量的命名
//m代表member,同时标清成员的类型和具体名称 int m_iAge; string m_strName;
成员函数及其参数的命名
//一般会有设定函数和读取函数,即set和get开头的函数,写参数时在前面加下划线 void setName(string _Name){m_strName = _Name;} string getName(){return m_strName;}
2. 规范编程
- 面向对象程序设计一般都使用多文件的项目,头文件写类的抽象声明,然后加一个同名的cpp文件写类的成员函数的定义
- 面向对象的程序设计的思想是让我们用功能函数去修改和输出成员变量的值,不推荐把成员变量写成公有的,直接通过赋值操作修改成员变量的值。有这样需求的话推荐使用结构体。
3. 构造函数相关
- 构造函数一般用来初始化数据成员
- 构造函数和类名同名,且没有返回值,在函数名前不加返回类型,直接写函数名和参数列表
- 构造函数允许重载,可以有多个构造函数
- 构造函数在对象实例化时被调用,如果有多个构造函数,只会根据参数调用其中一个且只调用一次
- 当用户没有定义构造函数时,编译器会自动生成一个构造函数(空函数)
- 构造函数同样可以给参数定义默认值,不过在使用默认参数时要避免和函数重载冲突
例如,有一个无参构造函数,有一个有两个参数的有参构造函数其两个参数都有默认值,在对象实例化时写0个参数,那么编译器不知道调用无参构造函数还是有默认值的有参构造函数二报错
4. 拷贝构造函数
顾名思义,拷贝构造函数就是用来拷贝对象的,下面是拷贝构造函数的自定义方式
class student { public: student(){} //普通构造函数 student(const student &stu){} //拷贝构造函数,有一个只读的引用参数 };
那什么时候会条用拷贝构造函数呢?
- 用另一个对象初始化或赋值一个对象时
student stu1; //调用普通构造函数 student stu2 = stu1; //调用拷贝构造函数 stu1 = stu2;
- 在一个对象作为函数参数时
5. 初始化列表
- 初始化列表就是用来初始化数据成员的,它几个有个特点:
- 先于构造函数之前初始化数据成员,且初始化的效率较高
- 必须以“:”隔开写在构造函数之后,多个数据成员用“,”隔开
- 以数据成员名后写“()”,括号内写初始化值的方式使用
代码示例
class student { public: student():name("Bob"),age(20) //初始化列表初始化name和age两个数据成员 {} private: string name; int age; };
6. 析构函数
- 析构函数是用来释放对象申请的资源的(如果对象申请了堆内存的话,就由析构函数来释放这块内存),在对象生命周期结束时自动调用。
代码示例
class MyClass { public: MyClass(){p = new int[10];} //构造函数(申请了堆内存) //析构函数 类名前加上“~”,其他特点和构造函数相同(无返回值,没有参数,和类名同名) ~MyClass(){delete[] p;} //析构函数(释放构造函数申请的堆内存), private: int* p; };
7. 其它需要注意的地方
- 类内的简单函数定义会被当成内敛函数编译,例如上面“成员函数及其阐述的命名”中的set函数就会被当成内联函数编译
- 内存分区:堆区、栈区、全局区(存储全局变量和静态变量)、常量区(存储常量和字符串)、代码区
- 当一个类实例化为一个对象时在栈中会开辟相应的内存,实例化多个对象后在栈区都会开辟相应的内存空间,但这些对象使用的成员函数在代码区只有一个,是共用的。
- 对象的声明历程(图片来源,慕课网)