类的深入剖析一
本章大纲
- 访问函数和工具函数
- 析构函数
- 对象复制和对象赋值
- 对象数组
- 对象指针
访问函数和工具函数
访问函数 :可以读取或显示数据
断言函数 :测试条件真假
工具函数 :也称为帮助函数,其访问属性为private,仅供类的其他成员函数调用,不提供给客户程序使用(非接口)。
析构函数
对象生存期结束时,系统自动调用析构函数,释放对象所占的内存空间
类的一个公有成员函数,不接受任何参数,由系统自动调用,不能重载
如果未定义析构函数,系统会自动生成默认析构函数
一般情况下,调用析构函数的次序正好与调用构造函数的次序相反
对象的存储类别可以改变调用析构函数的顺序
析构函数调用时机
全局定义的对象
- 在其他函数开始执行之前调用对象的构造函数
- 当main函数执行结束时,自动调用对应的析构函数
- 遇到exit和abort函数迫使程序立即结束,abort则不自动调用对象的析构函数
自动局部对象
- 定义对象时,调用构造函数;当对象的作用域结束时,调用析构函数
- 遇到exit和abort函数迫使程序立即结束,则不自动调用自动局部对象的析构函数
static 局部对象
- 构造函数在程序第一次执行到该对象定义处时被调用一次
- 当main函数执行结束时和遇到exit ,自动调用对应的析构函数
- abort函数迫使程序立即结束,则不自动调用static对象的析构函数
全局对象、静态对象的析构函数调用顺序与它们建立顺序正好相反
注意事项:
- 隐蔽陷阱—返回private数据成员的引用
- 应避免在函数中返回private数据成员的引用或指针,防止客户程序任意修改类的private数据
对象赋值
同类的对象之间可以互相赋值:即将一个对象中所有数据成员的值一一赋给另一个同类对象的对应成员
语法:
对象名1 = 对象名2;
注意:
- 对象名1和对象名2必须属于同一个类。
- 对象的赋值只对其中的数据成员赋值,而不对成员函数赋值。
- 类的数据成员中不能包括动态分配的数据,否则在赋值时可能出现严重后果。
对象复制
如果需要用到多个完全相同的对象,或将对象在某一瞬时的状态保留下来,可使用对象的复制机制
- 语法
- 形式一:
类名 对象2(对象1)
; - 形式二:
类名 对象名2 = 对象名1;
- 形式一:
//语法形式一:
类名 对象2(对象1);
Box box2(box1); (Box∷Box(const Box& b))//参数为Box对象的引用
//语法形式二:
类名 对象名2 = 对象名1;
Box box2=box1; //用box1初始化box2
复制构造函数:
- 将实参对象的各成员值一一赋给新的对象中对应的成员
- 参数只有一个且必须为本类的对象,采用对象的引用的形式
- 如果用户自己未定义复制构造函数,则编译系统会自动提供一个默认的复制构造函数,其作用只是简单地复制类中每个数据成员。
- 调用复制构造函数都是由编译系统自动实现的,不必由用户自己去调用。
区别
对象的赋值:对一个已经存在的对象赋值 对象的复制:从无到有地建立一个新对象,并使它与一个已有的对象完全相同(包括对象的结构和成员的值)
对象数组
在定义对象数组时,每一个数组元素即对象,都要调用构造函数。
对象数组可以进行初始化。如果类的构造函数只有一个参数,那么在定义数组时可以直接在等号后面的花括号内提供实参。
如果类的构造函数有多个参数,应该分别调用构造函数,对每个元素初始化,即采用在花括号中分别写出构造函数并指定实参的方法进行初始化。
student.h
#ifndef STUDENT_H
#define STUDENT_H
class Student {
public:
Student();
Student(double score);
Student(int no, int age, double score);
void Show();
private:
int no;
int age;
double score;
};
#endif
student.cpp
#include <iostream>
#include "student.h"
Student::Student() {
this->age = 18;
this->no = 1;
this->score = 0;
}
Student::Student(double score) { // 类的构造函数只有一个参数
this->score = score;
this->age = 18;
this->no = 1;
}
Student::Student(int no, int age, double score) {
this->no = no;
this->age = age;
this->score = score;
}
void Student::Show() {
std::cout << this->no << " " << this->age << " " << this->score
<< std::endl;
}
main.cpp
#include <iostream>
#include "student.h"
using namespace std;
int main() {
Student stus[3] = {55.5, 76.5, 95.5};
Student students[3] = { Student(1001, 18, 87),
Student(1002, 19, 76),
Student(1003, 18, 72)
};
stu[0].Show();
stu[1].Show();
stu[2].Show();
system("pause");
}
对象指针
在建立对象时,编译系统会为每一个对象分配一定的存储空间,以存放其成员。对象空间的起始地址就是对象的指针。可以定义一个指针变量,用来存放对象的指针
- 指向对象的指针
- 指向对象成员的指针
指向对象的指针
语法:类名 * 对象指针变量名;
用法:
*指针变量 //指针变量所指向的对象
(*指针变量).成员变量 //指针变量所指向的对象中的成员变量
指针变量->成员变量 //指针变量所指向的对象中的成员变量
(*指针变量).成员函数 //调用指针变量所指向的对象中的成员函数
指针变量->成员函数 //调用指针变量所指向的对象中的成员函数
int main() {
Student *ptr_stu;
Student *ptr[10];
Student **pptr;
Student ***ppptr;
ptr_stu=new Student(1004,25,95.5);
ptr_stu->Show();
(*ptr_stu).Show();
}
指向对象成员的指针
对象中的成员也有地址,存放对象成员地址的指针变量就是指向对象成员的指针变量
- 指向对象数据成员的指针:
对象数据成员数据类型名 *指针变量名;
- 指向对象成员函数的指针:
成员函数指针-声明
- 数据类型名 (类名∷*指针变量名)(参数表列);
- 编译系统要求指针变量的类型必须在下面3个方面与函数的类型相匹配:
- 函数参数的类型和参数个数;
- 函数返回值的类型;
- 所属的类
成员函数指针-赋值
指针变量名 = & 类名∷成员函数名;
成员函数指针-调用
( 具体对象 . * pointer ) ( 参数表 ) ;
int main() {
void (Student::*ptr_show)();//声明
ptr_show=&Student::Show;//赋值
Student student(98.5);
(student.*ptr_show)();//调用
}
本章总结
- 类的数据成员和成员函数属于该类的类域
- 在类域内,类的成员可以被该类的所有成员函数直接访问,并且可以按名字引用
- 在类域外,通过对象的句柄,对象名、对象的引用、指向对象的指针等方式引用类成员
- 工具函数是 private类型的成员函数,用来支持类的public成员函数的操作。
- 析构函数在删除对象时被隐式调用