第8章 类和对象
面向对象方法概述
类的声明和对象的定义
对象成员的引用
类的封装性和信息隐蔽
第9章 关于类和对象的进一步讨论
构造函数
析构函数
对象数组
对象指针
共用数据的保护
对象的动态建立和释放
对象的赋值和复制
静态成员
友元
类模板
第10章 运算符重载
什么是运算重载符
运算符重载的方法和规则
运算符重载函数作为类成员函数和友元函数
重载双目运算符
重载单目运算符
重载流插入运算符和流提取运算符
不同类型数据间转换
用类型转换函数进行类型转换
面向过程:适用于规模比较小的程序,其实是面向一个个函数实现一个个功能进行的,程序 = 算法 + 数据结构
面向对象:适用于规模比较大的程序,面向数据和函数封装成的对象,程序 = 多个对象 + 消息【对象 = 算法 + 数据结构】
对象:客观世界中的任何一个具体的事物都可以看做具体的对象,在C++中,对象由数据和函数组成
属性:对象的静态特征,例如班级的学生人数、所在教室
行为:对象的动态特征,例如班级开会等,行为由消息控制
封装:一是将数据和代码封装在一个对象中,二是将对象中某些部分对外隐蔽
抽象:类是对象的抽象,类是对象的模板,对象是类的特例,或者说对象是类的具体表现形式
继承与重用:继承就是能够利用之前的类再扩充一些功能生成一个新类,重用是软件重用
多态:给不同的对象发同一个消息,他们分别执行不同的动作叫多态
类是抽象的,不占用内存,而对象是具体的,占用内存,其实类就是一种广义上的数据结构
类的声明方法也是由结构体类型发展而来的,在C++中,二者甚至可以通用,区别在于未指定时类会默认private,而结构体会默认public,不过尽量采用class来建立类,private和public是成员访问限定符
类把数据和操作封装在一起了,一般来说类把数据隐藏起来,而把成员函数作为对外的接口
使用类函数注意事项:注意调用权限、注意函数的作用域(它能调用什么范围的数据和函数)
public内的成员函数是类的对外接口,private内的成员函数是类中其它成员的工具函数,类外的用户不能够调用
在类外也可以定义成员函数,举例如下:
class Student
{
public:
void display();
private:
int num;
string name;
char sex;
};
void Student::display()
{
cout<
注意上面的成员函数是怎么定义的 —— void Student::display() ,其中那两个冒号::代表这个函数属于这两个冒号前的那个类的类内成员,如果只有这两个冒号,或者什么都没有,那么说明这个函数不是成员函数,而是全局函数
一个对象所占用的空间只与该对象中的数据成员所占用的空间有关,而与成员函数无关,也就是说省了存类内函数的空间
类内成员的三种引用方式
(1)对象名.成员名
stu.num
(2)利用指针:
pStu->num //方式一
(*pStu).num //方式二
(3)利用引用:stu2是stu1的别名,它俩指的是同一块内存
Student stu1;
Studnet &stu2 = stu1;
对于C++来说,只要把类定义好,编写程序的工作就十分十分的简单了
类的作用就是把数据和算法封装在用户声明的抽象数据类型之中,用户主要通过调用公用的成员函数来实现类提供的功能(例如对数据成员赋值、显示数据成员的值、对数据进行加工等)
公用接口与私有实现的分离:例如在软件开发过程中,必须要实现两者的分离,这样的话只要类的接口没有改变,那么对私有实现的改变不会影响到程序的其它部分
如果一个类知识被一个程序使用,那么类的声明和成员函数的定义可以直接写在程序的开头,但是如果这个类被多个程序使用,那么这样的重复工作的量就太大了。一般在面向对象开发的时候,通用做法是把类的声明(含成员函数的声明)放在头文件里面,如果用户想要用这个类,那么就把这个头文件包进来就好啦。同时呢,为了信息隐蔽,对类成员函数一般不放在头文件中,而是另外放在一个问价当中。
类声明和成员函数定义的分离举例【共三个文件】:
文件一:头文件内进行类的声明:
//文件名:student.h
//这是个类声明头文件
#pragma once
#include
using namespace std;
class Student //类声明
{
public:
void display(); //公用成员函数声明
private:
int num;
string name;
char sex;
};
文件二:类成员函数的定义
//文件名:student.cpp
#include
#include "student.h" //注意包含这个类声明头文件
using namespace std;
//在本文件中进行函数的定义
void Student::display() //注意这里的两个冒号很重要
{
cout << num << endl;
cout << name << endl;
cout << sex << endl;
}
文件三:主函数
//文件名:main.cpp
#include
#include "student.h" //包含这个类声明头文件
using namespace std;
int main()
{
Student stu; //定义一个对象
stu.display(); //执行对象的display函数,这里你点那个点就会发现其它的成员都访问不了
return 0;
}
对象是谁?stu
方法是谁?display()
消息是谁?stu.display()
简单来说,构造函数就是处理对象的初始化。需要注意的是类的数据成员是不能在声明类的时候初始化的,因为类只是一种数据类型,而不是真正的对象。
构造函数举例:
#include
using namespace std;
class Time
{
public:
Time() //定义构造成员函数,函数名与类名相同,通过构造函数对对象中的数据成员赋初值
{
hour = 0;
minute = 0;
sec = 0;
}
void set_time();
void show_time();
private:
int hour;
int minute;
int sec;
};
//定义成员函数用于赋值
void Time::set_time()
{
cin >> hour;
cin >> minute;
cin >> sec;
}
//定义成员函数用于输出
void Time::show_time()
{
cout << hour << ':' << minute << ':' << sec;
}
//主函数
int main()
{
Time t; //建立对象t,同时调用构造函数t.Time()进行对象初始化
//t.set_time();
t.show_time();
return 0;
}
除此之外可以采用参数初始化表进行数据成员的初始化
构造函数重载:在一个类中可以定义多个构造函数,以便对类对象提供不同的初始化选择,这些构造函数具有相同的名字,但是参数的个数或者参数的类型不相同,这就称为构造函数的重载,但是一个类只有一个默认构造函数
析构函数前面有一个 ~ 符号,其作用与构造函数相反,析构函数的作用并不是删除对象,而是在撤销对象占用内存之前完成一些清理工作。一个类可以有很多的构造函数(重载),但是只有一个析构函数。先构造的后析构,后构造的先析构,类似于一个栈
数组不仅仅可以由简单变量组成,也可以由对象组成
对象所占用的空间的首地址就是对象的指针
(1)指向对象中数据成员的指针
定义方法与结构体一样
(2)指向对象中函数成员的指针
普通函数的指针变量:void (* p) ( ); 这里p是指向void类型函数的指针变量
指向对象成员函数的指针变量:void (Time:: * p)( ); 这里p是指向Time类中公用成员函数的指针变量
怎样定义呢?举例:p = &Time::get_time; 注意这里的get_time只是函数名,没有括号哦
保证数据能够在一定范围内共享,又要保证它不被任意修改,这时可以使用const把有关数据定义为常量
动态建立和释放对象的举例:【类似于 malloc() 和 free() 】
Box * pt; //定义一个 Box * 类型的指针变量 pt
pt = new Box; //在 pt 中存放新建的对象的首地址
delete pt; //释放 pt 所指向的内存空间
复制 = 新建 + 赋值
静态数据成员
如果希望各个对象中的某个数据成员都是一致的,那么在类中可以这样定义:
static int height;
静态成员函数
与静态数据成员相类似
static float sum();
友元就是介于公用和私有之间的一种东西。假如有个函数定义在了本类外(可以是非成员函数,也可以是其它类的成员函数)那么在类体中用 friend 对这个函数进行声明,这个函数就称为本类的友元函数,这个友元函数可以访问这个类的私有成员。
举例:
#include
using namespace std;
class Time
{
public:
Time(int,int,int);
friend void display(Time &); //声明display为Time类的友元函数
private:
int hour;
int minute;
int sec;
};
//定义构造函数用于赋初值
Time::Time(int h,int m,int s)
{
hour = h;
minute = m;
sec = s;
}
//定义成员函数用于输出
void display(Time & t) //t是Time类对象的引用,这个函数是友元函数
{
cout << t.hour << ':' << t.minute << ':' << t.sec;
}
//主函数
int main()
{
Time t(10,13,20);
display(t); //t是Time类对象
return 0;
}
类模板使用举例:
#include
using namespace std;
template
class Compare
{
public:
Compare(numtype a, numtype b)
{
x = a;
y = b;
}
numtype max()
{
return(x > y) ? x : y;
}
numtype min()
{
return(x < y) ? x : y;
}
private:
numtype x, y;
};
int main()
{
Compare cmp_1(3, 7);
cout << cmp_1.max() << " is the max of two numbers" << endl;
Compare cmp_2(45.6, 98.7);
cout << cmp_2.min() << " is the min of two numbers" << endl;
return 0;
}
所谓的重载,就是重新赋予新的含义。函数重载就是一名多用,同一个函数名可以用来代表不同功能的函数;运算符重载我们其实也是一直在用(例如我们用+进行整数、浮点数等等的运算,其实这个就是运算符重载)
所谓的运算符重载就是赋予运算符新的意义