第四章类与对象
41面向对象程序设计的基本特点
4.1.1抽象
:对具体问题(对象)进行概括,抽出一类对象的公共性质并加以描述的过程。
分数据抽象与功能(行为)抽象
4.1.2封装
:将抽象得到的数据与行为相结合形成一个整体(类)
//类----封装
class Clock
{
//行为抽象
public:
void SetTime(int newH, int newM, int newS);
void ShowTime();
//数据抽象
private:
int hour, minute, second;
};
4.1.3继承
把特殊与一般的概念间的关系描述清楚,使特殊概念之间既能共享一般的属性与行为,又具有特殊的属性与行为。
继承
:提供类的继承,允许保持原有类特性的基础上进行更具体的详细说明(第七章)
4.1.4多态
:指一段程序能处理多种类型对象的能力,可通过强制多态,重载多态,类型参数化多态,包含多态,4中方法实现(第八章,第九章细说)
4.2类和对象
4.2.1类的定义
类使面向对象程序设计方法的核心,类可以实现对数据的封装和隐蔽,是对问题的抽象描述,描述一类对象的共同属性与行为
//类----封装
class Clock
{
//函数成员
public://外部接口
void SetTime(int newH, int newM, int newS);
void ShowTime();
protected://保护型成员
private://私有数据成员
int hour, minute, second;
4.2.2类成员的访问控制
类成员:函数成员与数据成员
访问控制属性分:共有类型,保护类型,私有类型
共有类型是外部接口(public)
私有成员只能被本类的成员函数访问
保护与私有类型相似但是在继承过程中产生新类的影响不同
修饰访问类型的关键字可以多次出现
class Clock
{
//函数成员
public://外部接口
void SetTime(int newH, int newM, int newS);
public :
void ShowTime();
private://私有数据成员
int hour, minute, second;
};
4.2.3对象
:该类的实体
对象所占据的内存空间只用于存放数据成员,函数成员在内存只占据一份空间
class Clock
{
//函数成员
public://外部接口
void SetTime(int newH, int newM, int newS);
public :
void ShowTime();
private://私有数据成员
int hour, minute, second;
};
int main()
{
Clock myclock;
myclock.SetTime;
4.2.4类的成员函数
1实现
class Clock
{
//函数成员
public://外部接口
void settime(int newH, int newM, int newS);
public :
void ShowTime();
private://私有数据成员
int hour, minute, second;
};
//返回类型 类名::函数名(参数)
void Clock::settime(int newH, int newM, int newS)
{
hour = newH;
minute = newM;
second = newS;
}
2成员函数调用中的目的对象
如上myclock.settime,myclock就是目的对象。
3带默认形参值的成员函数
只能在类中定义
class Clock
{
//函数成员
public://外部接口
void settime(int newH=0, int newM=0, int newS=0);
public :
void ShowTime();
private://私有数据成员
int hour, minute, second;
};
4内联成员函数
显示声明与隐式声明
class Clock
{
//函数成员
public://外部接口
void settime(int newH=0, int newM=0, int newS=0);
public :
//隐式内联函数
void ShowTime()
{
cout << hour << ":" << minute << ":" << second << ":";
};
private://私有数据成员
int hour, minute, second;
};
//显示声明,在类定义不用加showtime
inline void Clock::ShowTime()
{
cout << hour << ":" << minute << ":" << second << ":";
}
4.2.5完整实例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class h
{
public:
//带参数的函数成员只能在类中
void a(int h1=0, int m1=0, int s1=0);//时间置零
//隐式内联
void b()
{
cout << h << ":" << m << ":" << s << ":"<<endl;
};
private:
int h, m, s;//时分秒
};
//设置时间
void h ::a(int h1, int m1, int s1)
{
h = h1;
m = m1;
s = s1;
}
int main()
{
h myh;
myh.a();//置零
myh.b();//显示置零
myh.a(8, 30, 30);//设置为8小时30分30秒
myh.b();//显示
}
现象
:
4.3构造函数与析构函数
4.3.1构造函数
作用
:在对象被创建时利用特定的值构造对象,将对象初始化为一个特定的状态
构造函数与类名相同
没有返回值
系统会自动生成隐含的构造函数
class a {
a() {};//编译系统生成的隐含的默认构造函数
};
这个构造函数不做任何事情,为什么要生成?
(还要负责基类的构造与成员对象的构造)
class a {
a() {};//编译系统生成的隐含的默认构造函数
a()//无参的构造函数
{
h = 0;
m = 0;
s = 0;
}
a(int h1, int m1, int s1);//有参的构造函数
private:
int h, m, s;
};
//构造函数的实现
a::a(int h1, int m1, int s1)
{
h = h1;
m = m1;
s = s1;
}
4.3.2复制构造函数
在下面三种情况调用
(1)用类的对象去初始化另一个对象
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Point
{
public:
//(内联)构造函数
Point(int xx = 0, int yy = 0) {
x = xx;
y = yy;
}
//复制构造函数
Point(Point& p);
int getx() { return x; };
int gety() { return y; };
private:
int x, y;
};
Point::Point(Point& p) {
x = p.x;
y = p.y;
}
int main()
{
Point a(1, 5);
//复制构造函数的调用,两个方式等效
Point b(a);
Point c = a;
cout << b.getx() << endl;
return 0;
}
(2)如果函数参数是类的对象,调用函数时,进行形参与实参的结合时
#include<iostream>
using namespace std;
class Point
{
public:
//(内联)构造函数
Point(int xx = 0, int yy = 0) {
x = xx;
y = yy;
}
//复制构造函数
Point(Point& p);
int getx() { return x; };
int gety() { return y; };
private:
int x, y;
};
Point::Point(Point& p) {
x = p.x;
y = p.y;
}
void f(Point p) {
cout << p.getx() << endl;
}
int main() {
Point a(1, 2);
f(a);
return 0;
}
(3)如果函数的返回值时类的对象,函数执行完成返回调用者时
#include<iostream>
using namespace std;
class Point
{
public:
//(内联)构造函数
Point(int xx = 0, int yy = 0) {
x = xx;
y = yy;
}
//复制构造函数
Point(Point& p);
int getx() { return x; };
int gety() { return y; };
private:
int x, y;
};
Point::Point(Point& p) {
x = p.x;
y = p.y;
}
Point g() {
Point a(1, 2);
return a;
}
int main() {
Point b;
b = g();
cout << "b.getx=" << b.getx() << endl;
return 0;
}
显示复制构造函数与隐式区别,可以自己控制内容,而且当类的数据成员中有指针类型时,隐式的复制构造函数只能浅复制,会带来数据安全隐患。
4.3.3析构函数
:与构造函数刚好相反,完成对象被删之前的清理工作,如果希望程序在对象被删除前自动完成某事,可以把他们写到析构函数中
4.3.4程序实例
4.4类的组合
4.4.1组合
一个类内嵌其他类的对象作为成员的情况,关系时包含与被包含
当创建类的对象时,如果这个类具有内嵌对象成员,,那么各个内嵌对象将首先被自动创建。
在创建对象时既要对本类的基本类型数据成员进行初始化,又要对内嵌对象成员进行初始化。
创造一个组合类对象时,构造函数顺序
(1)内嵌对象的构造函数,调用顺序按照内嵌对象在组合类的定义中出现的次序
(2)调用本类构造函数的函数体
析构函数刚好相反
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cmath>
using namespace std;
class Point {
public:
Point(int xx = 0, int yy = 0) {
x = xx;
y = yy;
}
Point(Point& p);
int gety() { return y; };
int getx() { return x; };
private:
int x, y;
};
Point::Point(Point& p) {
x = p.x;
y = p.y;
}
//类的组合
class line {
public:
line(Point xp1, Point xp2);
line(line& l);
double getlen() { return len; };
private:
Point p1, p2;
double len;
};
//类的组合的实现
line::line(Point xp1, Point xp2):p1(xp1),p2(xp2) {
double x = static_cast<double>(p1.getx() - p2.getx());
double y = static_cast<double>(p1.gety() - p2.gety());
len = sqrt(x * x + y * y);
}
//组合类的复制构造函数
line::line(line& l) :p1(l.p1), p2(l.p2) {
len = l.len;
}
4.4.2前向引用声明
class b;//前向引用声明
class a {
public:
void f(b b1);
};
class b {
public:
void g(a a1);
};
在提供一个完整的类前,不能定义该类的对象,可以使用类的对象引用或者指针
class b;//前向引用声明
class a {
public:
void f(b b1);
/*错误
b类的对象在定义前被使用
void abc() {
x.abcd();
}*/
//错误,b类定义不完善
b b1;
private:
b& x;//b类对象的引用或指针
};
class b {
public:
void g(a a1);
void abcd();
};
4.5UML图形标识(略)
4.6结构体与联合体
4.6.1结构体
结构体是一种特殊形态的类,区别在于两者具有不同的默认访问控制属性:未指定访问控制属性的成员,类是private结构体是public
如果一个结构体的全部数据成员都是公共成员,并且没有用户定义的构造函数,没有基类和虚函数,可以这样赋值
#include<iostream>
#include<iomanip>
#include<string>
using namespace std;
struct student {
int num;
string name;
char sex;
int age;
};
int main()
{
student stu = { 97001,"lin lin",'F',29 };
}
4.6.2联合体(public)
联合体的全部数据成员共享同一组内存单元。(联合体变量中的成员同时至多只有一个有意义)
联合体的对象成员,不能有自定义的构造函数,析构,重载的复制赋值运算符
联合体不能继承,不支持包含多态
一般用联合体储存一些共有数据,而不是定义函数成员
成员项具有相同的地址
无名联合体
union {
int i;
float f;
};
int main()
{
i = 10;
}
使用联合体保存信息
#include<iostream>
#include<string>
using namespace std;
class examinof {
public:
examinof(string name, char grade)
:name(name), mode(GRADE), grade(grade) {};
examinof(string name, bool pass)
:name(name), mode(PASS), pass(pass) {};
examinof(string name, int percent)
:name(name), mode(PERCENTAGE), percent(percent) {};
void show();
private:
string name;
enum {
GRADE,
PASS,
PERCENTAGE
}mode;
union {
char grade;
bool pass;
int percent;
};
};
void examinof::show() {
cout << name << ":";
switch (mode) {
case GRADE:cout << grade;
break;
case PASS:cout << (pass ? "PASS" : "FAIL");
break;
case PERCENTAGE:cout << percent;
break;
}
cout << endl;
}
int main()
{
examinof course1("english", 'b');
examinof source2("calculus", true);
examinof source3("c++", 85);
course1.show();
source2.show();
source3.show();
return 0;
}