C++面向对象- -类和对象的基础认识

目录

类的声明和对象的定义

定义对象的方法

类的成员函数

1、内置成员函数

2、成员函数的存储方式

对象成员的引用

1、通过对象名和成员运算符访问对象中的成员

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

3、通过对象的引用来访问对象中的成员

类和对象的简单应用

类声明和成员函数定义的分离


一些啰嗦的概念,了解了解就行:

对象:客观世界中任何一个事物都可以看作是一个对象。

任何一个对象都应具有属性(变量/数据)和行为(函数/操作代码)这两个要素,对象应能根据外界给的信息进行相应的操作(功能的实现)。一个对象一般是由一组属性和一组行为构成,而一组数据是与一组操作对应的。

面向对象程序设计的一个重要特点是:“封装性” 。所谓封装性有两方面含义:一是将有关的数据和操作代码封装在一个对象中,形成一个基本单位,各个对象之间相对独立,互不干扰。二是将对象中某些部分对外隐蔽,即隐藏其内部细节(私有属性或者保护属性),只保留少数接口(公有属性),以便与外界联系,接受外界的消息。这种对外界隐蔽的做法称为信息隐蔽,信息隐蔽有利于数据安全,防止无关的人了解和修改数据。

类是一个对象的类型,是对象的抽象,对象是类的特例,或者说是类的具体表现形式。类是抽象的,不占用内存,而对象是具体的,占用内存存储空间,类只是给出一种 “模型” ,供用户定义实际的对象。类是所有面向对象语言的共同特征,都提供有 类 这种类型,如果一种编程语言不包含类,它就不能称为面向对象的语言。

多态性:由继承而产生的相关的不同的类,其对象对同一消息会做出不同的响应。这和后边的继承与派生有所关联,也经常使用到,多态性是面向对象程序设计的一个重要特性,增加程序的灵活性。C++语言支持两种多态性:编译时的多态性和运行时的多态性,编译时的多态性是通过函数重载和运算符重载来实现的;运行时的多态性是通过虚函数来实现的,后边具体解释。

在面向对象程序设计中有几个名词:行为、对象、方法、消息。行为:体现为类的成员函数,在面向对象程序理论中也称为“方法”;方法:指的是对数据的操作,一个方法对应一种操作,显然只有声明为公用的方法(成员函数)才能被外界所激活;消息:就是一个命令,要求对象执行的一个操作,调用一个对象的方法(如 t.set() )就是一个发给对象的消息,外界就是通过发送消息来激活相关的方法。

 

类的声明和对象的定义

对于类,在类外不可访问其非公有属性的数据成员和成员函数,但一般情况是把数据隐蔽起来,把成员函数作为对外界的接口。在一个类中应当至少有一个公用的成员函数,作为对外的接口,否则就无法对对象进行任何操作。关于类类型的声明,有其一般形式:

class 类名{
	private:
		私有的数据和成员函数。 
	public:
		公用的数据和成员函数。
	//protected:
		//受保护的数据和成员函数。
		//后边类的继承和派生处用到。 
};

private 和 public 称为成员访问限定符 ,用它们来声明各成员的访问属性 。如果在类的定义中既不指定 private ,也不指定 public ,系统默认是私有属性的,也正是由于C++增添了 class 类型后 ,前边的结构体类型(struct)仍保留着,而且将它的功能扩展了,也允许用struct 去声明一个类,和类(class)的定义一样,但在用的结构体中,虽然也可以设置访问属性,但默认情况下是公用属性。用 struct 声明的结构体类型实际上也就是类。在大多情况下,都把所有数据指定为私有,以实现信息隐蔽。

private 声明为私有的成员,只能被本类中的成员函数所引用,类外不能调用(友元函数除外);public 声明为公用的成员,既可以被本类中的成员函数引用,也可以被类的作用域内的其他函数所引用;此外还有一种成员访问限定符 protected (受保护的),用protected 声明为受保护的成员,它也不能在类外被访问(这点与私有成员类似),但可以被派生类的成员函数访问

在一个类体中, 关键字 private 和 public 可以分别出现多次 , 即一个类体中可以包含多个 private 和 public 的部分,但为了使程序清晰,应养成一个习惯:使每一种成员访问限定符在类定义体中只出现一次。我的习惯是那些私有的我省去了 private 这个关键字,知道是私有的就行。

 

定义对象的方法

  • 先声明 类 类型,然后再定义对象。

1): class 类名 对象名 ;

class Student std ;

2): 类名 对象名 ;

Student std ;
  • 在声明类的同时定义对象。

class Student{
		int num ;
		char sex ;
		string name ;
	public:
		void display(){
			cout << "num=	" <<num <<endl;
			cout << "sex=	" <<sex <<endl;
			cout << "name=	" <<name <<endl;
		}
}std1 , std2;

 

  • 不出现类名,直接定义对象。

class {
		int num ;
		char sex ;
		string name ;
	public:
		void display(){
			cout << "num=	" <<num <<endl;
			cout << "sex=	" <<sex <<endl;
			cout << "name=	" <<name <<endl;
		}
}std1 , std2;

 

这定义方法看个人习惯,我比较喜欢先定义好一个类之后,在主函数中定义对象,这种定义还是比较清晰的。

 

类的成员函数

一般把外界需要调用的函数指定为 public ,作为类的对外接口使用 ,但有的函数并不是准备对外调用的,而仅仅作为本类中的成员函数所调用,那就应该指定为 private ,这种函数的作用是支持其他函数的操作,是类中其他成员的工具函数,用户不能去调用。但像一般学习的过程中,很少用到这种工具函数,毕竟写的程序不会太大,除非去做一个大的项目。

 

类体中定义函数时,不需要在函数名前加类名,因为函数属于哪一类很明显,但如果在类外定义时,必须在函数名前加上类名,予以限定是哪个类中的成员函数。“::” 是作用域限定符或称作用域运算符 ,用它来声明函数属于哪个类。

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

如果在作用域运算符 “::” 的前面没有类名,或者函数名前面既无类名又无作用域运算符,如 “ ::display()” 或 “ display() ” 表示该函数不属于任何类,不是成员函数,而是全局函数,即一般函数。

 

1、内置成员函数

在类体中定义的成员函数的规模一般都很小,而系统调用函数的过程所花费的时间开销相对于是比较大的,调用一个函数的时间开销远远大于小规模函数体中全部语句的执行时间,故为了减少时间开销,如果在类体中定义的成员函数中不包括循环等控制结构,系统会自动对它们作为内置(inline)函数来处理。用 inline 声明的作用是在系统调用这些函数时,并不是真正执行函数的调用过程,而是把函数代码嵌入程序的调用点,而若不用 inline 声明,在调用时先去函数代码段的入口地址,执行完该函数代码段后再返回函数调用点,可见用 inline (内置)函数可以大大减少调用成员函数的时间开销。

对一般的内置函数要用关键字 inline 声明 ,但对类体内定义的成员函数 ,可以省略 inline ,因为这些成员函数已默认指定为内置函数。如:

class Student{
		int num ;
	public:
		void display(){	// inline void display()
			cout << "num=	" <<num <<endl;
        }
};

对在类体内定义的函数,一般都省略inline ,但如果成员函数不在类体内定义,而在类体外定义,系统并不把它默认成内置函数,调用这些成员函数的过程和调用一般函数的过程是相同的,如果仍想把这些成员函数指定为内置函数,应当用 inline 做显式声明。如:

class {
		int num ;
	public:
		inline void display();	//声明为内置函数 
};
inline void Student :: display(){	//类外定义函数为内置函数 
	cout << "num=	" << num <<endl;
}

这样不利于类的接口与类的实现分离,不利于信息隐蔽,虽然程序的执行效率提高了,但从工程质量角度来看,这并不是好方法,只有在类外定义的成员函数规模很小且调用频率较高时,才指定为内置函数。此处也就是随口提了一句,后面基本上不做 inline 的声明,现在了解一下即可。

 

2、成员函数的存储方式

对于同一类的不同对象中的数据成员的值一般是不相同的,而不同对象的函数的代码是相同的,不论调用哪一个对象的函数的代码,其实调用的都是同样内容的代码,因为在计算机中人们用一段空间来存放这个共同的函数的目标代码,在调用各对象的函数时,都去调用这个公共的函数代码。这样做也大大节约了存储空间,因此每个对象所占用的存储空间只是该对象的数据成员所占用的存储空间,而不包括函数代码所占用的存储空间。尝试一下:

#include <iostream>
using namespace std ;
class Student{
		int i ;
		char j ;
	public: 
		void display() ;
};
int main(){
	cout << sizeof(Student) <<endl;
}

证明了一个对象所占的空间大小只取决于该对象中数据成员所占的空间,与成员函数无关,这并不是说函数不占用存储空间,而是说函数的目标代码是存储在对象空间之外。下面说一下一些需要注意的方面:

  1. 不同的对象使用同一个函数代码段,那是如何分别对不同对象中的数据进行操作呢?系统专门设有一个叫 “ this ” 的指针,用于指向不同的对象,当调用对象 std1 的成员函数时, this 指针就指向了对象 std1 ,成员函数访问的就是 std1 的成员,此处就略微介绍一下 this 的情况,后面还有关于 this 的进一步的说明。
  2. 不论成员函数是在类体内定义还是在类外定义,成员函数的代码段的存储方式是相同的,都不占用对象的存储空间。
  3. 不要把成员函数的存储方式和 inline (内置)函数的概念混淆 ,不论是否用 inline 声明 ,成员函数的代码段都不占用对象的存储空间, inline 函数只影响程序的执行效率,而与成员函数是否占用对象的存储空间无关 。
  4. 既然成员函数的代码段不在对象的存储空间中,但“对象 std 的成员函数 display() ” 这样的说法仍是对的,因为从逻辑的角度上看,成员函数是和数据一起封装在一个对象中,只允许本对象中成员函数去访问同一对象中的私有数据。

 

对象成员的引用

访问对象中的成员有三种方法:1)、通过对象名和成员运算符访问对象中的成员;2)、通过指向对象的指针访问对象中的成员;3)、通过对象的引用访问对象中的成员。

 

1、通过对象名和成员运算符访问对象中的成员

成员运算符 : “ . ” 

其一般形式为: 对象名.成员名 。如:

#include <iostream>
using namespace std ;
class Student{
		int i ;
	public: 
		int j ;
		void display() ;
};
void Student::display(){
	cout << "j= " << j <<endl;
}
int main(){
	Student s ;
	s.j=1 ;
	s.display();
}

 这都是在类外访问,而在类外只能访问 public 成员 。

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

需要定义一个指向类 类型的指针。如:

Student s , *p ;
p=&s ;
p->j=1 ;
p->display(); 

这样写的结果同上一样。对于指针的访问,还可以有 (*p). j  。

3、通过对象的引用来访问对象中的成员

引用:引用与其代表的对象共享同一存储空间,它们代表的是同一个对象,只是用不同的名字表示。用作引用的变量不再分配新空间,另外需要注意的是在定义引用时就必须对其进行初始化,不然会出错。如:

Student s1 ;
Student &s2=s1 ;        //定义Student类引用s2,并初始化为s1
s2.j=1 ;
s2.display();

 

类和对象的简单应用

我写一段短的类和对象的使用,尽可能用的功能多一点。

#include <iostream>
using namespace std ;
class Time{
		int hour ;
		int min ;
	public:
		int sec ;
		void set();
		void show(){
			cout<<"时间是:" << hour << ":" << min << ":" << sec << endl; 
		}
};
void Time::set(){
	cout<<"输入时、分、秒 :" << endl;
	cin>>hour>>min ;
}
int main(){
	Time t ;
	t.set();
	cin>>t.sec ;
	t.show() ;
}

下面是把对象作为一个普通函数的形参来表示。

#include <iostream>
using namespace std ;
class Time{
		int hour ;
		int min ;
	public:
		int sec ;
		void set(){
			cout<<"输入时、分 :" <<endl;
			cin>>hour>>min ;
		}
		void show();
};
void Time::show(){
	cout<<"时间是:" << hour << ":" << min << ":" ; 
}
void show(Time &t){
			cout<<t.sec<<endl;
		}
void set(Time &t ,  int s){
	t.sec=s ;
}
int main(){
	Time t ;
	int s ;
	t.set();
	cout<<"输入秒: "<<endl;
	cin>>s;
	set(t,s);     //传进去一个“默认”参数。
	t.show();
	show(t);
}

写的太不正规了,这只是为了看的更清晰一点,如果自己对这方面都掌握的话,写在程序中别像这样,看着很乱。另外在写的时候会发现用对象作为一个普通函数的形参时,在函数体中只能对对象的公用成员进行操作,其 private 成员已经是属于在类外访问,不可取。注意区分什么场合用域运算符“::”,什么场合用成员运算符“.”,不要搞混。另外对于一些新学的知识,最好是自己把自己的想法都上机演示一下,看看自己想的有没有毛病。

 

类声明和成员函数定义的分离

如果一个类被多个程序使用,往往把类的声明(包含对其成员函数的声明)放在指定的头文件中,在使用该类时,只要把有关的头文件包含进来即可, 减少工作量,提高了编程的效率。另外为了实现之前说的信息隐蔽,不让用户看到函数执行的细节,那么再把对类成员函数的定义不和类的声明放一个头文件中,而是放在另一个文件,此时注意在系统提供的头文件中只包括对成员函数的声明,不包括成员函数的定义,类的声明和定义是分别放在两个文件中。

实际上,一个C++程序是由 3 个部分组成:(1)类声明头文件(后缀为 .h 或者无后缀);(2)类实现文件(后缀为 ,cpp ),包括对类成员函数的定义;(3)类的使用文件(后缀为 .cpp ),即主文件。下面解释一下:

这是主函数写的实现代码。这块的文件名是“main.cpp” ,可见在这函数板块中,引入了“time.h”头文件后,有关类的声明和类内部实现的功能都未显示,但这个程序是正确的,原因就是刚才说的将类声明和成员函数实行了分离。

//#include <iostream>        //不需要输入输出,没有相关语句
#include "time.h"     //将类声明的头文件包含进来
int main(int argc, char** argv) {
	Time t;
	t.set();
	t.show();
	return 0;
}

这部分是类的声明文件“time.h” ,仅有类的定义、成员的声明,而没有成员函数的定义,这就很好地实现的信息的隐蔽性。

class Time{
		int hour;
		int min;
		int sec;
	public:
		void set();
		void show();
};

这是有关类成员函数的定义,由于这个文件中需要进行输入和输出,相关的头文件也要注意写。

#include <iostream>
#include "time.h"        //此处不要忘记引入
using namespace std;
void Time::set(){
	cout<<"设置时间:"<<endl;
	cin>>hour>>min>>sec ;
}
void Time::show(){
	cout<<"时间显示:"<<endl;
	cout<<hour<<":"<<min<<":"<<sec<<endl;
}

但要注意的是,如果想对类的声明和成员函数定义进行分离的话,需要建一个项目,这些文件需要在同一个项目中,因为这样系统能知道哪个是源文件、头文件,不然这种实现方式是不成功的,或许文件保存时改一下类型也有可能正确,但我不是很清楚,有兴趣的可以去钻研一下。

在实际工作中,并不是将一个类声明做成一个头文件,而是若干个常用的功能相近的类声明集中在一起,形成类库。类库有两种:一种是系统提供的标准类库;另一种就是用户根据自己的需要做成的用户类库,提供给自己或者其他人使用,这称为自定义类库。类库包括两个组成部分:(1)类声明头文件;(2)已经过编译的成员函数的定义,它是目标文件。我们只需把类库装入到自己的计算机系统中(一般是编译系统所在的子目录下,项目下边就是类似的情况),然后在程序中用“#include”指令将有关的类声明的头文件包含进来就可以使用。在程序开发工作中,类库很有用,所以对我们学生来说,先了解一下就好。

 

 

 

猜你喜欢

转载自blog.csdn.net/qq_43305922/article/details/85338165