【918C++程序设计】7、类与对象

7.1面向对象的程序设计

4个特点:抽象、封装、继承、多态性(有时只选3个)

什么是面向对象的程序设计

1、对象
客观世界中任何一个事物都可以看成一个对象。
一个班级作为一个对象时有两个要素:
静态特征:学生人数、所在教室——属性
动态特征:开会、体育比赛——行为(功能)

任何一个对象都具有两个要素,即属性和行为,对象是由一组属性和一组行为构成的。
在C++中,每个对象都是由数据和函数组成的,数据体现了“属性”,函数是用来对数据进行操作的,就是“行为”,也叫“方法”。

2、封装与信息隐蔽
可以对一个对象进行封装处理,把它的一部分属性和功能对外界屏蔽,也就是说从外界是看不到的、甚至是不可知的。
C++类对象中的函数名就是对象的对外接口,外界可以通过函数名调用这些函数来事项某些功能。

3、抽象
抽象的作用是表示同一类食物的本质。
类是对象的抽象,而对象则是类的特例,即类的具体表象形式。

4、继承与重用
父类/基类:“马”
子类/派生类:“白马”
采用继承的方法可以很方便地利用一个已有的类建立一个新的类,大大节省了编程工作量。这就是常说的“软件重用”的思想。

5、多态性
由继承而产生的不同的派生类,其对象对统一消息会作出不同的响应。

7.2 类与对象的概念与设计

类和对象的关系

类是对象的抽象,而对象是类的具体实例。
类是抽象的,不占用内存,而对象是具体的,占用存储空间。

声明类类型

不声明则默认为private

class 类名{
private:
私有的数据和成员函数;
public:
公用的数据和成员函数;
};

还有一种成员访问限定符protected,它不能被类外访问,但能被派生类的成员函数访问。

定义对象的方法

1、先声明类类型,再定义对象
(1)class 类名 对象名
(2)类名 对象名

2、在声明类的同时定义对象

class Student{
}stu1,stu2;

3、不出现类名,直接定义对象(不提倡)

class{
}stu1,stu2;

成员函数

“::”是作用域限定符/作用域运算符
类名::类成员函数

class Student{
public:
void display();
};
void Student::display(){}

内置成员函数

在类体中定义的成员函数中不包括循环等控制结构,C++系统自动地对它们作为内置函数来处理。程序调用这些函数时把函数代码嵌入程序的调用点。减少时间开销。
PS:函数只有在类体中定义时,才会自动作内置函数。若函数在类体外定义,但又想将其指定为内置函数,应用inline作显式声明,且声明和定义应在同一个文件。

对象成员的引用

1、通过成员运算符
形式:对象名.成员名

2、通过指向对象的指针

Time t,*p;
p = &t;
cout<<p->hour;

3、通过对象的引用

Time t1;
Time &t2 = t1;
cout<<t2.hour;

7.3 构造函数和析构函数

对象的初始化

如果一个类中所有的成员都是公用的,则可以在定义对象时对数据成员进行初始化

Time t1={10,22,36};

但如果数据成员是私有的,或者类中有private或protected的数据成员,就不能这样初始化。

构造函数:一种特殊的成员函数,不需要用户来调用它,而是在建立对象时自动执行。
构造函数必须与类名同名,构造函数是在声明类的时候定义的,程序用户只需在定义对象的同时指定数据成员的初值。

PS:
1)构造函数没有返回值因此没有类型
2)可以用一个类对象初始化另一个类对象
3)构造函数中可以包含其他(非赋值)语句,如cout,但不建议
4)如果用户没有定义构造函数,系统会自动生成,但是该函数体为空,没有参数,不执行初始化操作

1、不带参数的构造函数

class Time{
	private:
		int hour;
		int minute;
	public:
		Time(){
			hour = 0;
			minute = 0;
		}
};
int main(){
	Time t1;
	//此时t1的hour,minute为0
}

2、带参数的构造函数

class Time{
	private:
		int hour;
		int minute;
	public:
		Time(int h,int m){
			hour = h;
			minute = m;
		}
};
int main(){
	Time t1(10,23);
	//此时t1的hour为10,minute为23
}

3、用参数初始化表对数据成员初始化
不在函数体内对数据成员初始化,而是在函数首部实现。
在原来函数首部的末尾加一个冒号,然后列出参数的初始化表。后面的花括号是空的,即函数体为空。减少函数体的长度,使结构函数精炼简单。
类名::构造函数名(参数表):成员初始化表{}

class Time{
	private:
		int hour;
		int minute;
	public:
		Time(int h,int m):hour(h),minute(m){}
};
int main(){
	Time t1(10,23);
	//此时t1的hour为10,minute为23
}

构造函数的重载

在一个类中可以定义多个构造函数,以便为对象提供不同的初始化方法,这些构造函数名相同,但参数个数/参数类型不同。
PS:建立对象时不必给出实参的构造函数称为默认构造函数,无参构造函数属于默认构造函数。一个类只能由一个默认构造函数,若用户未定义,系统提供一个,但函数体为空。

使用默认参数的构造函数

class Time{
	private:
		int hour;
		int minute;
	public:
		Time(int h=10,int m=23){
			hour = h;
			minute = m;
		}
};

如果构造函数的全部参数都指定了默认值,则在定义对象时,可以给或不给或给几个实参。
由于不需要给出实参也可以调用构造函数,因此全部参数都指定了默认值的构造函数也属于默认构造函数
一个类中定义了全部都是默认参数的构造函数后,不能再定义重载构造函数

析构函数

“~”位取反运算符,
形式:~类名
当对象的生命期结束时,会自动执行构造函数。析构函数的作用并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作不能被重载。
一个类可以有多个构造函数,但只能有一个析构函数,还可以用来执行“用户希望在最后一次使用对象之后所执行的任何操作"。

class Student{
	public:
		~Student(){
			cout<<"调用析构函数!"<<endl;			
		}
}; 

特殊情况:
常数据成员,只能通过构造函数的参数初始化列表对常数据成员进行初始化,其他任何函数都不能对常数据成员赋值。
如果将成员函数声明为常成员函数,则只能引用本类中的数据成员,而不能修改它们。

调用构造函数和析构函数的顺序

一般情况下,调用析构函数的次序正好与调用构造函数的次序相反:最先被调用的构造函数,其对应的(同一对象中的)析构函数最后被调用。
先构造的后析构,后构造的先析构

7.4复制构造函数

形式:类名 对象2(对象1)
复制构造函数也是构造函数,但它只有一个参数,这个参数是本类的对象。

class Time{
	private:
		int hour;
		int minute;
	public:
		一般约定加const声明,使参数值不能改变,以免在调用此函数时不慎使实参对象被修改
		Time(const Time &t){
			hour = t.hour;
			minute = t.minute;
		}
}; 
int main(){
	Time t2(t1);//建立一个新对象t2
	如果用户未定义复制构造函数,系统自动提供一个默认的复制构造函数,
	但作用只是简单地复制类中每个数据成员
	
	Time t3 = t1;//此方法效果一样
}

需要复制构造函数的情况:
1)程序中需要建立一个对象,并用另一个同类对象对其初始化。
2)当函数的参数为类的对象时。
3)函数的返回值是类的对象。

7.5 共有数据的保护

既要使数据能在一定范围内共享,又要保证它不被任意修改,此时把有关的数据定义为常量。

常对象

形式:
类名 const 对象名(实参)
const 类名 对象名(实参)

定义常对象时,必须同时初始化,之后不能再改变。

const Time t1(10,23);

常对象只能调用它的常成员函数,常成员函数是常对象唯一的对外接口。
常成员函数可以访问常对象中的数据成员,但不允许修改常对象中的数据成员的值。
PS:若有需要,一定要修改常对象中的某个数据成员,则将该数据成员声明为mutable

mutable int count;

常对象成员

1、常数据成员
形式:const 类型名 变量名
只能通过构造函数的参数初始化表对常数据成员进行初始化

class Time{
	private:
		const int hour;
	public:
		Time(int h){
			hour = h;
		}//非法
		Time(int h):hour(h){}//合法

2、常成员函数
形式:类型名 函数名(参数表)const
只能引用本类中的数据成员,但不能修改它们。不能调用另一个非const的成员函数

指向对象的常指针

形式:*类名 const 指针变量名
指针变量始终保持为初值,不能改变,即指向不变

Time t1(10,23),t2;
Time *const ptr;
ptr = &t1;
ptr = &t2;//非法,ptr已指向t1,不能改变
但可以改变t1的值

指向常对象的指针变量

形式:const 类型名 * 指针变量名
1)如果一个变量已被声明为常变量,只能用指向常变量的指针变量指向他。
2)指向常变量的指针变量除了可以指向常变量,还可以指向未被声明为const的变量,此时不能通过此指针变量改变该变量的值。
3)如果函数的形参是指向普通变量的指针变量,实参只能用指向普通变量的指针。

当指针指向常对象时,同理,只需将“常变量”替换为“常对象”。
当希望在调用时对象的值不被修改,就把形参定义为只指向常对象的指针变量,同时用对象的地址作实参(对象是否const都行)
PS:若定义了一个指向常对象的指针变量,是不能通过它改变所指向的对象的值,但是指针变量本身的值是可以改变的。

对象的常引用

如果不希望在函数中修改实参的值,可以把函数的形参声明为const

void fun(const Time &t){}

const型数据小结

形式含义
Time const t;t是常对象,其值任何情况下不能改变
void Time::fun()const;常成员函数,可以引用,但不能修改本类中的数据成员
Time *const p常指针变量,p的指向不能改变
const Time *p指向常对象的指针变量,p指向的类对象不能通过p改变
const Time &t1 = tt1是t的引用,二者指向同一存储空间,t的值不能改变

7.6 静态成员

静态数据成员

如果希望各对象中的数据成员的值一样,将其定义为静态数据成员,这样它就为各对象所共有,所有对象都可引用 ,在内存中只占一份空间
静态数据成员可以初始化,但只能在类体外。

class Box{
	public:
		static int height;
		int width;
};
int Box::height = 10;

静态数据成员既可以通过对象名引用,也可以通过类名引用。
但在类外可以直接访问静态数据成员的前提是,其为public

Box.height;
box1.height;

静态成员函数

静态成员函数没有this指针(根本区别),因此不能访问本类中的非静态成员,但可直接引用本类中的静态成员。
C++中静态成员函数主要用来访问静态数据成员。

class Box{
	private:
		static int height;
		int width;
	public:
		static float fun1();
};
float Box::fun1(){
	return height/2;
}

7.7 友元

提前说明,友元的关系是单向不传递的。
a认为b是朋友,b不一定认为a是朋友;b是a的朋友,c是b的朋友,c不一定是a的朋友。

提前引用

涉及一个额外知识点,提前引用:
在正式声明一个类之前先声明一个类名,表示此类将稍后声明,只包含类名,不包括类体。
PS:类的提前声明的作用范围是有限的,只有在正式声明以后才能用它定义类对象。

友元函数

在本类外的地方定义了一个函数(可以是别的类的函数或者不属于任何类),在类体中用friend对其进行声明,此函数就成为本类的友元函数。
友元函数可以访问这个类的私有成员。
1、将普通函数声明为友元函数
2、友元成员函数(把另一类的成员函数声明为friend)

友元类

将B类声明为A的友元类,此时友元类B中的所有函数都是A类的友元函数,可以访问A类中的所有成员。
形式:friend 类名;

7.8 类模板

功能是相同的,仅仅是数据类型不同
声明模板前形式:template<class 虚拟类型参数>
定义对象形式:类模板名<实际类型名>对象名(参数表);
类外定义成员函数形式:
template<class 虚拟类型参数>
函数类型 类模板名<虚拟类型参数>::成员函数名(形参){}

template<class T>
class Compare{
	private:
		T a,b;
	public:
		Compare(T a,T b){
		...
		}
		print(){
		...
		}
};
int main(){
	Compare<int> com1(3,4);
	Compare<float> com1(3.5,4.7);		

7.9 运算符的重载

运算符重载的特点与使用

运算符重载的规则
1)不允许用户自己定义新的运算符。
2)不能重载的运算符有5个:"." , “*” , “::” , “sizeof” , “?:”
3)重载运算符不能改变运算符运算对象的个数、不能改变优先级、不能改变结合性
4)不能有默认的参数
5)重载的运算符必须和用户定义的自定义类型的对象一起使用,其参数至少有一个是类对象
6)用于类对象的运算符一般必须重载,但有两个例外,运算符"=","&"

“<<",">>",和类型转换运算符只能定义为友元函数重载
一般将单目运算符和复合运算符重载为成员函数
一般将双目运算符重载为友元函数

几种常用的运算符重载的写法

class Complex{
	private:
		double real;//实部 
		double imag;//虚部
	public:
		Complex(){
			real = 0;
			imag = 0;
		}
		Complex(double r,double i){
			real = r;
			imag = i;			
		}
		
		friend Complex operator +(Complex &a,Complex &b); 
		friend istream &operator >>(istream &in,Complex &a);
		friend ostream &operator <<(ostream &out,Complex &a);
		
		void display(){
			cout<<real<<"+"<<imag<<'i'<<endl;
		}
};


Complex operator +(Complex &a,Complex &b){
			Complex c;
			c.real = a.real + b.real;
			c.imag = a.imag + b.imag;
			return c;
}

istream &operator >>(istream &in,Complex &a){
	in>>a.real>>a.imag;
	return in;
}

ostream &operator <<(ostream &out,Complex &a){
	out<<a.real<<"+"<<a.imag<<'i'<<endl;
	return out;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值