类和对象
类的定义
类定义的通常结构为:
class Box
{
private: // 访问修饰符
// 定义数据类型
double length;
double breadth;
double height;
public:
// 定义类型成员
typedef int a_type;
// 成员函数声明
double get(void);
void set(double len, double bre, double hei);
};
// 成员函数定义
double Box::get(void)
{
return length * breadth * height;
}
void Box::set(double len, double bre, double hei)
{
length = len;
breadth = bre;
height = hei;
}
成员函数的两种定义方式
在类体内定义
在类体内定义的函数默认是内联函数.
内联函数:在编译时候,若普通函数A()调用了内联函数inline B(),则在编译时,在A()中,会使用B()的函数体来替换原本使用B()的地方.而普通函数则需要在运行A()时,才会使用函数调用机制进行调用.内联函数是一种空间换时间的方法,通过使用内联函数,减少了函数调用次数,但是却增加了编译后的体积(只要有调用内联函数B()的地方,就会进行展开).
内联函数和宏定义共同之处是都需要在使用时进行展开,而不是进行函数调用.区别在于: 1: 时间不同,宏定义在预处理时进行替换,内联函数在编译时进行替换 2: 功能不同:内联函数会检查参数类型,所以更安全
内联函数用的地方不多,它会将函数调用处用函数体替代,所以一般在类体内部对成员函数作声明,而在类体外部进行定义.
如果函数体较小,且用的比较多,那么定义内联函数也挺好.
在类体外通过::定义
::被称为域解析符(也称作用域运算符或作用域限定符),用来连接类名和函数名,指明当前函数属于哪个类。(命名空间也是用这个解析符)
class Student{
public:
//成员变量
char *name;
int age;
float score;
//成员函数
void say(); //函数声明
};
//在类体外进行函数定义
void Student::say(){
cout<<name<<"的年龄是"<<age<<",成绩是"<<score<<endl;
}
构造函数与析构函数
类的构造函数可以使用两种方式进行定义:
- 常规写法
- 初始化列表
常规写法
#include <iostream>
using namespace std;
class Student{
private:
char *m_name;
int m_age;
float m_score;
public:
//声明构造函数
Student(char *name, int age, float score);
//声明普通成员函数
void show();
};
//定义构造函数
Student::Student(char *name, int age, float score){
m_name = name;
m_age = age;
m_score = score;
}
使用初始化列表方式定义构造函数
//采用初始化列表方式定义构造函数
Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){
//TODO:
}
//定义普通成员函数
void Student::show(){
cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl;
}
创建对象
int main(){
//在栈上创建对象时向构造函数传参
Student stu("小明", 15, 92.5f);
stu.show();
//在堆上创建对象时向构造函数传参
Student *pstu = new Student("李华", 16, 96);
pstu -> show();
return 0;
}
两种定义方式的区别
- 两种方式效率相同,但是如果类有成员常量(
const),就需要使用初始化列表方式 - 初始化常量方式以
:为标志
成员函数的定义规则
常量成员函数
如果将const关键字放在函数的参数列表后面,表示该函数为常量成员函数.example:int max(a,b) const{}.常量成员函数的一个最重要的特点就是:无法通过常量成员函数来修改成员变量的值.如果要通过常量成员函数修改成员变量,则编译器会报错.
const对象只能调用常量成员函数.
建议:但凡是不能要求修改成员变量的函数,都设置成常量成员函数.
对象的定义
在不同的内存区域定义对象
| 定义对象的位置 | 是否使用new | 返回类型 | 特点 |
|---|---|---|---|
| 栈 | 不用new | 对象本身 | 在栈上创建一个对象,可以直接使用对象. |
| 堆 | 使用new | 对象的指针 | 使用new关键字在堆上创建对象,使用后需要使用delete删除不再使用的对象,避免遗留大量垃圾 |
使用不同的方式进行对象创建
以此为例,介绍使用隐式调用,显式调用和new()来创建对象
class Student{
private:
char *m_name;
int m_age;
float m_score;
public:
//声明构造函数
Student(char *name, int age, float score);
//声明普通成员函数
void show();
};
//采用初始化列表方式定义构造函数
Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){
//TODO:
}
创建对象方式
| 初始化方式 | 使用自定义构造函数 | 使用默认构造函数 |
|---|---|---|
| 显式调用构造函数 | Student stu = Student("张三",20,98.0); | Student stu = Student(); |
| 隐式调用构造函数 | Student stu("张三",20,98.0); | Student stu; |
| new创建 | Student *stu =new Student("张三",20,98.0); | Student * stu = new Student; |
explicit关键字
explicit是用来修饰类的构造函数的,被修饰的构造函数将不能通过隐式调用的方式创建对象,只能通过显式调用等方式创建.
其注意事项为:
- 只能用于类内部定义的构造函数(默认是内联的)
- 只能用于单参数的构造函数
继承
不同继承方式子类的权限
class 派生类名:[继承方式] 基类名{
派生类新增加的成员
};
继承方式:public,protected,private.默认是private.
不同的继承方式会影响父类的成员在子类中的访问权限,不同继承方式指定子类继承父类的成员时对成员的最高访问权限
| 继承方式 | 父类权限 | 子类权限 |
|---|---|---|
| public | public | public |
| protected | protected | |
| private | 不能继承 | |
| protected | public | protected |
| protected | protected | |
| private | 不能继承 | |
| private | public | private |
| protected | private | |
| private | 不能继承 |
使用using关键字可以在子类中使用父类中的成员,改变父类中成员访问权限.
//基类People
class People {
public:
void show();
protected:
char *m_name;
int m_age;
};
//派生类Student
class Student : public People {
public:
void learning();
public:
using People::m_name; //将protected改为public
using People::m_age; //将protected改为public
float m_score;
private:
using People::show; //将public改为private
};
接口(抽象类)
c++中,使用抽象类来实现接口. 抽象类:如果类中有至少一个遗憾被声明为纯虚函数,则该类就是抽象类.
纯虚函数:函数声明中,原型尾部有=0,如:
class Box{
public:// 纯虚函数
virtual double getVolume() = 0;
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度};
抽象类中,其声明的纯虚函数必须在子类中实现.
多继承
class D: public A, private B, protected C{
//类D新增加的成员
}
子类成员函数规则
子类与父类函数不构成重载,只要函数名相同,无论参数列表如何,子类的函数就会覆盖父类定义的函数.
- 类的构造函数不能被继承,但是可以调用父类的构造函数来简化对象构造过程.
- 父类构造函数优先调用
- 派生类构造函数中只能调用直接基类的构造函数,不能调用间接基类的
- 调用析构函数时,优先调用子类的析构函数,其次才是父类的析构函数
总结
- class内部成员默认是
private,struct成员默认是public