类和对象(C++学习笔记 16)

  • 类(class)是一种数据类型,是一种用户定义的抽象的数据类型。
  • 类是一种抽象的数据类型,它不占存储空间,不能容纳具体的数据,因此在类声明中不能给数据成员赋初值。
  • 类代表了一批对象的共性和特征。
  • 类是进行封装和数据隐藏的工具,它将数据与操作紧密地结合起来。
  • 类是对象的抽象,对象是类的实例。
  • C++保留C结构体这种数据类型,并对其功能进行了扩充,使结构体可以含有不同类型的数据,而且还可以含有函数。类与结构体的扩充形式非常相似。
  • 类与结构体区别:在结构体中,如果对成员不作private 或public声明,系统将其默认为公有的( public ),外界可以任意的访问其中的数据成员和成员函数,它不具有信息隐蔽的特性;而在类的声明中,如果对其成员不作private 或public声明,系统将其默认为私有的( private ),外界不可以访问其中的数据成员和成员函数,它提供了默认的安全性。
  • 一般来说,公有成员是类的对外接口,而私有成员是类的内部数据和内部实现,不希望外界访问。

一、结构体

结构体--------统一操作一个整体对象。

结构体类型的定义形式:
struct [结构体类型名]
{
  数据类型 成员1;
  数据类型 成员2;
  ···
  数据类型 成员n;
};

说明:
(1) 在需要该结构体变量的地方,用“ struct 结构名 变量名 ”进行变量定义。若省略结构名,则只能在类型定义的同时定义结构体变量,在程序的其他地方无法使用该结构体定义变量。

结构体变量的定义:

1)先定义结构体类型,再定义结构体类型变量。

struct stu_rec{
	char cno[8];  //学号
	char cname[10];  //学生姓名
	char csex;   //性别
	int iscore[4];   //四科考试成绩
};
struct stu_rec student1,student2;   //定义结构体类型变量

通常,这样的结构体类型定义放在函数的外部,程序的开始部分或头文件中,变量的定义在函数中。

2)定义结构体类型的同时定义结构体类型变量。

struct date{
	int iyear;
	int imonth;
	int iday;
}mydate1,mydate2;

在这种方式下,结构体类型定义通常在函数的外部,程序的开始部分。其后也可以使用1)的方式再定义其他结构体变量。

3)直接定义结构体类型变量。

struct
{
	char cno[8];
	char cname[10];
	char csex;
	float fb_salary,ff_salary,fp_salary;   //基本工资、浮动工资、奖金
}person1,person2;

这种方式灵活性差,一般不用。

(2) 结构体类型中的成员类型可以是基本数据类型,也可以是其他数据类型。同一类型的成员可以在同一行定义,形式为:数据类型 成员名1,成员名2······

(3) 存储一串字符应将其定义为字符数组

(4) 结构体变量的成员引用
使用成员运算符“ . ”,引用的形式为:
<结构体类型变量名> . <成员名>

(5) 结构体类型变量的初始化
由于结构体类型变量可以汇集不同数据类型的成员,所以结构体类型变量的初始化必须在结构体类型变量的定义时进行。
例如: struct stu_info student = { “23333”, “xiao ming”, “f”, “20”,“computer science”}

(6) 指向结构体的指针
结构体变量所占内存单元的起始地址就是该变量的指针。
可以定义一个指针变量来指向该结构体变量,并通过该指针变量来引用结构体变量成员。
1)结构体指针的定义
struct  结构体类型名 *指针变量名
例1:

struct stu_info *q;    //q为指针变量,指向结构体类型stu_info

例2:

struct stu_cj
{
	char cno[8];
	char cname[10];
	float fscore[4];
}stud,*p1,*p2;  //p1,p2为指针变量,指向结构体类型stu_cj

变量的地址:&stud
成员的地址:&stud.cno、&stud.cname、&stud.fscore[0]

2)结构体指针对成员的引用
引用形式为:
指针变量->成员;
( *指针变量 ). 成员

例如: 指针变量p1、p2指向结构体变量x:p1=p2=&x;
    通过结构体指针p1、p2来引用结构体变量x成员,以下三种方式是等价的:
    x.cno、x.cname、x.fscore[0]
    p1->cno、p1->cname、p1->fscore[0]
    (*p2).cno、(*p2).cname、(*p2).fscore[0]

二、结构体的扩充

使结构体可以含有不同类型的数据,而且还可以含有函数
例如,下面声明了一个扩充的结构体Complex,用这个扩充的结构体类型求复数的绝对值:

#include<iostream>
#include<cmath>
using namespace std;
struct Complex{
	double real;  //复数的实部
	double imag;  //复数的虚部
	void init(double r,double i)  //定义函数init,给real和imag赋初值
	{
        	real=r;
        	imag=i;
        }
        double abscomplex(){  //定义函数abscomplex,求复数的绝对值
 		double t;
 		t=real*real+imag*imag;  
 		return sqrt(t);
 	}
};
int main()
{
	Complex A;
	A.init(1.1,2.2);
	cout<<"复数的绝对值是:"<<A.abscomplex()<<endl;
	return 0;
}

在这个结构体中,real 和imag 是数据成员,函数init 和 abscomplex 是成员函数。

三、类的声明

类与结构体的扩充形式十分相似。C++规定,在默认情况下,类中的成员是私有的,结构体中的成员是公有的。

(1)类声明的一般形式如下:

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

(2)类中的成员:

私有成员(用private声明): 包括数据成员和成员函数,只能被类内的成员函数访问,而不能被类外的对象访问。这样,私有成员就整个隐蔽在类中,在类的外部,对象无法直接访问它们,实现了访问权限的有效控制。
公有成员(用public声明): 既可被类内的成员函数访问,也可被类外的对象访问。这部分的数据成员和成员函数称为类的公有成员,公有成员对外是完全开放的,公有成员既可以被本类的成员函数访问,也可以在类外被该类的对象访问,公有成员函数是类与外界的接口,来自类外部的对私有成员的访问需要通过这些接口来进行。
保护成员(用protected声明): 可以由本类的成员函数访问,也可以由本类的派生类的成员函数访问, 而类外的任何访问都是非法的,即它是半隐蔽的。

说明:

  • 如果一个类的所有成员都声明为私有的,即只有私有部分,那么该类将完全与外界隔绝,这样,虽然数据“安全”的目的达到了,但是这样的类是没有实际意义的。在实际应用中,一般把需要保护的数据设置为私有的,把它隐蔽起来,而把成员函数设置为公有的,作为类与外界的接口。
  • 一般情况下,一个类的数据成员应该声明为私有成员,成员函数声明为公有成员。这样,内部的数据隐蔽在类中,在类前面的外部无法直接访问,使数据得到有效的保护,也不会对该类以外的其余部分造成影响,程序模块之间的相互作用就被降低到最小。
  • 类声明中的 private、protect 和 public 三个关键字可以按任意顺序出现任意次,但是,如果把所有的私有成员和公有成员归类放在一起,程序更加清晰。
  • 数据成员可以是任何数据类型,但不能是自动(auto)、寄存器(register)或外部(extern)进行说明。

auto/register/extern表示的是变量的存储位置和作用域。auto变量存储在函数的堆栈空间,register存储在寄存器,extern表示这里没有新定义变量,只是扩展了一个已有全局变量的作用域。类和结构体中的变量是成员变量,其存储位置和作用域由定义对象的函数决定,不由这些对象自己决定。

(3)成员函数的定义

在C++中,成员函数既可以定义为普通的成员函数(即非内联的成员函数),也可以定义为内联成员函数。以下有3种定义方式,方式1是定义普通成员函数,方式2和3是定义成内联成员函数。

1)定义方式1:
在类声明中只给出成员函数的原型,而将成员函数的定义放在类的外部。(即类内声明,类外定义)

这种成员函数在类外定义的一般形式为:
返回值类型 类名::成员函数名(参数表)
{
   函数体
}

例如, 表示坐标点的类Point 可以声明如下:

#include<iostream>
using namespace std;
class Point{
	public:
		void setpoint(int,int);  //坐标点的成员函数setpoint的函数原型
		int getx();   //取x坐标点的成员函数getx的函数原型 
		int gety();   //取y坐标点的成员函数gety的函数原型 
	private:
		int x,y; 
};
void Point::setpoint(int a,int b)  //在类外定义成员函数setpoint
{
	x=a;
	y=b;
} 
int Point::getx()  //在类外定义成员函数getx
{
	return x;
} 
int Point::gety()  //在类外定义成员函数getx
{
	return y;	
}

说明:
①“::”是作用域运算符,用于声明这个成员函数是属于哪个类的,如“Point::”,说明这些成员函数是属于类Point的。

2)定义方式2:
将成员函数直接定义在类的内部。

此时,C++编译器将函数setpoint、getx、gety作为内联函数进行处理,即将这些函数隐含地定义为内联成员函数。由于没有使用关键字inline,因此成为隐式定义。

例如,

class Point{
	public:
		void setpoint(int a,int b)  //坐标点的成员函数setpoint的函数原型
		{
			x=a;
			y=b;
		}
		int getx()   //取x坐标点的成员函数getx的函数原型 
		{
			return x;
		}
		int gety()   //取y坐标点的成员函数gety的函数原型 
		{
			return y;
		}
	private:
		int x,y; 
};

3)定义方式3:

在类声明中只给出成员函数的原型,而将成员函数的定义放在类的外部,但是在类内函数原型声明前或在类外定义成员函数前加上关键字“inline”,来显式地说明这是一个内联函数。称为显示定义。

例如,

class Point{
	public:
		inline void setpoint(int a,int b);  //声明函数setpoint为内联函数 
		inline int getx();   //声明成员函数getx为内联函数 
		inline int gety();   //声明成员函数gety为内联函数 
	private:
		int x,y; 
};
inline void Point::setpoint(int a,int b){
	x=a;
	y=b;
}
inline int Point::getx(){
	return x;
}
inline int Point::gety(){
	return y;
}

四、类和对象的关系

在C++中,把具有相同数据和相同操作集的对象看成是同一类。
一个类也就是用户声明的一个数据类型。在程序中定义的每一个变量都是其所属数据类型的一个实例。类的对象可以看成是该类类型的一个实例,定义一个对象和定义一个一般变量相似。

五、对象的定义

可以用一下两种方法定义对象:

(1)在声明类的同时,直接定义对象

即在声明类的右花括号“ } ” 后,直接写出属于该类的对象名表。
例如,在声明类Point 的同时,直接定义了对象op1 和op2。

class Point{
	public:
	  void setpoint(int,int);
	  int getx();
	  int gety();
	private:
	  int x,y;
}op1,op2;

(2)声明了类之后,在使用时再定义对象

定义的一般形式为:类名 对象名1 对象名2 ······

例如,定义op1和op2为Point 类的两个对象:

Point op1,op2;

六、对象中成员的访问

公有的数据成员和成员函数,在类的外部可以通过类的对象进行访问。访问对象中的成员通常有三种方法:

(1)通过对象名和对象选择符(“.”,简称为点运算符)访问对象中的成员。

一般形式为:对象名.数据成员名对象名.成员函数名[(实参表)]

例如,访问对象中的成员

#include<iostream>
using namespace std;
class Point{
	public:
		inline void setpoint(int a,int b);  //声明函数setpoint为内联函数 
		inline int getx();   //声明成员函数getx为内联函数 
		inline int gety();   //声明成员函数gety为内联函数 
	private:
		int x,y; 
};
inline void Point::setpoint(int a,int b){
	x=a;
	y=b;
}
inline int Point::getx(){
	return x;
}
inline int Point::gety(){
	return y;
}

int main(){
	Point A;
	A.setpoint(2,3);
	cout<<A.getx()<<" "<<A.gety()<<endl;
	return 0;
}

(2)通过指向对象的指针访问对象中的成员

class Date{
	public:
		int year;
};
int main(){
	Date d,*ptr;   //定义对象d和指向类Date的指针变量ptr
	ptr=&d;  //使ptr指向对象d
	cout<<ptr->year;  //输出ptr指向对象中的成员year
	return 0;
}

在这个例子中,
d.year ,(*ptr).year ,ptr->year 这三者是等价的。

(3)通过对象的引用访问对象中的成员

为一个对象定义了一个引用,也就是为这个对象起了个别名,因此完全可以通过引用来访问对象中的成员,其方法与通过对象名来访问对象中的成员是相同的。

例如,

class Date{
	public:
		int year;
};
int main(){
	Date d1;
	Date &d2=d1;
	cout<<d1.year;
	cout<<d2.year;
}

七、类的作用域和类成员的访问属性

类的作用域: 指在类的声明中的一对花括号所形成的作用域。一个类的所有成员都在该类的作用域内。在类的作用域中,一个类的任何成员可以不受限制地访问该类中的其他成员。而在类的作用域外,对该类的数据成员和成员函数的访问则要受到一定的限制,有时甚至是不允许的,这与类成员的访问属性有关。

类成员的访问属性: 说明为公有的成员不但可以被类中的成员函数访问,还可在类的外部,通过类的对象进行访问;说明为私有的成员只能被类中的成员函数访问,不能在类的外部,通过类的对象进行访问。

一般来说,公有成员是类的对外接口,而私有成员是类的内部数据和内部实现,不希望外界访问。

猜你喜欢

转载自blog.csdn.net/aaqian1/article/details/83795185