【C++期末开卷资料】1 类与对象

1 类与对象

1.1 类

1.1.1 类的定义

  • 类是具有相同属性和方法的一类对象集合的抽象,它包含数据抽象(即数据成员)和行为抽象(即成员函数)。

  • 定义类的过程就是对问题进行抽象和封装的过程。

一般形式为:

class 类名{
	public:
    	<共有数据和函数>
    protected:
    	<保护数据和函数>
    private:
    	<私有数据和函数>
}

1.1.2 类成员的访问控制

  • 成员缺省定义为private的。私有成员是被隐藏的数据,只有该类的成员函数或友元函数才可以访问它。

  • 保护成员也不能通过对象访问,但是可以被该类或派生类的成员函数访问。

  • 共有成员定义了类的外部接口。

1.1.3 成员函数的实现

  • 成员函数的实现,可以放在类体内;也可以放在类体外,但必须在类体内给出原型说明
  • 放在类体内定义的函数被默认为内联函数,而放在类体外定义的函数是一般函数,如果要定义为内联函数则需在前面加上关键字inline。

在类体外定义成员函数的一般形式为:

<返回类型> <类名> :: <成员函数名>(<参数说明>)
{
	类体	
}

1.1.4类的界面和实现

为了减少代码的重复,加快编译速度,在大型程序设计中,C++的类结。构常常被分为两部分:

一部分是类的界面,另一部分是类的实现

1.2 对象

1.2.1 对象的声明

对象是类的实例或实体。

一般格式为:

	<类名><对象名>;

例如,声明类Point的对象:

Point p1,p2;
Point p[3];//定义了三个Point类型的对象数组
Point *p3;//定义了一个Point类型的指针
Point &rp=p1;//给p1取了一个别名rp

1.2.2 对象成员的访问

  1. 圆点访问方式
对象名.成员名 或 (*指向对象的指针).成员名
  1. 指针访问方式
对象指针变量->成员名 或 (&对象名)->成员名

1.3构造函数与析构函数

1.3.1 构造函数

构造函数是一种特殊的成员函数,对象的创建和初始化工作可以由它完成,其格式如下:

<类名>::<类名>(<形参表>)
{
	<函数体>    
}
  1. 构造函数的特点:

    • 被声明为公共
    • 函数名与类名相同
    • 可以重载
    • 不能指定返回类型
    • 不能被显式调用
  2. 默认构造函数

    默认构造函数就是无参数的构造函数。既可以是自己定义的,也可以是编译系统自动生成的。

    当没有为一个类定义任何构造函数的情况下,编译系统就会自动生成无参数、空函数体的默认构造函数。

    其格式如下:

    <类名>::<类名>()
    {
    
    }
    

1.3.2 成员初始化表

带有成员初始化表的构造函数的一般形式如下:

类名::构造函数名([参数表])[:(成员初始化表)]
{
	//构造函数体
}

成员初始化表的一般形式为:

	数据成员名1(初始值),数据成员名2(初始值2),...

eg.

class Simple
{
   int x;
   int &rx;//引用
   const float pi;//const
public:
    Sample::Sample(int x1):x(x1),rx(x),pi(3,14f){
    //x=x1'
}

1.3.3 具体默认参数的构造函数

如果构造函数的参数值通常是不变的,只有在特殊形况下才需要改变它的参数值,这时可以将其定义为带默认参数的构造函数

1.3.4 析构函数

  1. 析构函数有如下特点:

    • 只能被声明为共有函数。
    • 析构函数的名字同类名,与构造函数名区别在于析构函数名前加~,表明它的功能与构造函数的功能相反。
    • 析构函数没有参数,不能重载,一个类中只能定义一个析构函数。
    • 不能指定返回类型。
    • 析构函数在释放一个对象的时候被自动调用。
  2. 默认析构函数

    如果一个类中没有定义析构函数,系统将自动生成一个默认析构函数,其格式如下:

    <类名>::~<类名>()
    {
    
    }
    

1.3.5 拷贝构造函数

  1. 拷贝构造函数的定义

    拷贝构造函数是一种特殊的构造函数,它的作用是用一个已经存在的对象去初始化另一个对象

    其格式为:

    <类名>::<类名>(const <类名>&<类名>)
    {
    	函数体
    }
    

2.拷贝构造函数的特点

  • 拷贝构造函数名字与类相同,不能指定返回类型
  • 拷贝构造函数只有一个参数,该参数是该类的对象的一个引用。
  • 它不能被显示调用。

3.在以下情况会被自动调用

  • 当用类的一个对象去初始化该类的另一个对象时
  • 当函数的形参是类的对象,进行形参和实参的结合时
  • 当函数的返回值时类的对象,函数执行完成返回调用者时

调用拷贝构造函数的三种情况

(1)当用类的一个对象去初始化该类的另一个对象时。

如:

Point p2(p1);//用对象p1初始化对象p2,拷贝构造函数被调用(代入法)
Point p3=p1;//用对象p1初始化对象p3,拷贝构造函数被调用(赋值法)

(2)当函数的形参时类的对象,调用函数,进行形参和实参结合时。

如:

Fun1(Point p)//函数的形参是类的对象
{
	p.print();
}
int main(){
	Point p1(10,20);
	Fun1(p1);//当调用函数,进行形参和实参结合时
	return 0}

(3)当函数的返回值时类的对象,函数执行完成,返回调用者时.

如:

Point Fun2(){
	Point p1(10,30);
	return p1;//函数返回值是对象
}
main(){
	Point p2;
	p2=Fun2();//函数执行完成,返回调用者时
	return 0;
}

1.3.6 浅拷贝和深拷贝

所谓浅拷贝,就是用默认的拷贝构造函数实现数据成员逐一赋值。

需要自己写拷贝构造函数,实现额外的内容,这种称为深拷贝。

如:(如果不进行深拷贝,一个空间会被多次析构,发生错误)

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstring>
using namespace std;
class String
{
	char* sbuf;//用来存放字符串的指针
	int length;//表示字符串长度
public:
	String()
	{
		length = 0;//串长为0
		sbuf = new char;
		sbuf[0] = '\0';
	}
	String(const char* s)//用字符初始化
	{
		length = strlen(s);//计算字符串长度
		sbuf = new char[length + 1];//开辟空间,对空间放'\0'字符串结束符
		strcpy(sbuf, s);
	}
	String(const string& s)
	{
		length = s.length;

		//sbuf=s.sbuf
		sbuf = new char[length+1];
		strcpy(sbuf, s);
		cout << "我重写的拷贝构造函数" << endl;
	}
	~String() {//析构函数,回收空间
		delete[] sbuf;
	}
	void Show()//输出字符串
	{
		cout << sbuf << endl;
	}
};
int main() {
	String s1("Hello");//定义一个对象,调用构造函数
	String s2 = s1;
	s1.Show();
	return 0;
}

1.4 this指针

  • this指针是由c++编译自动产生且较常用的一个隐含对象指针,它不能被显式声明。
  • this指针是一个局部变量,局部于某个对象
  • this指针是一个const指针,不能修改它或给它赋值

this的用途

  1. 为了区分成员和非成员

    CDate(int year = 2009, int month = 1, int day = 1)
    	{
    		this->year = year;
    		(*this).month = month;
    		this->day = day;
    	}
    
  2. 类的方法需要返回当前对象的引用

1.5 向函数传递对象

  1. 使用对象作为函数参数
  2. 使用对象指针作为函数参数
  3. 使用对象引用作为函数参数(可加const防止被修改)
class Point
{
	int x;
	int y;
public:
	Point(int a, int b):x(a),y(b){}
	void set(int a, int b)
	{
		x = a;
		y = b;
	}
	void Show() {
		cout << x << " " << y << endl;
	}
};
void Fun1(Point point)//Point point = p 
{
	point.set(10, 10);
	cout << "point1 is x and y are:" << endl;
	point.Show();
}
void Fun2(Point *point)//Point *point = &p
{
	point->set(10, 10);//p.set(10,10);
	cout << "point2 is x and y are:" << endl;
	point->Show();
}
void Fun3(Point &point)//Point &point = p 引用=别名
{
	point.set(10, 10);//p.set(10,10);
	cout << "point3 is x and y are:" << endl;
	point.Show();
}
int main() {
	Point p(1, 2);
	cout << "The original p is" << endl;
	p.Show();//1 2
	Fun1(p);//使用对象作为函数参数 10 10
	cout << "But,p is unchanged in main:" << endl;
	p.Show();//1 2
	Fun2(&p);//使用对象指针作为函数参数 10 1
	cout << "p is changed in main:" << endl;
	p.Show();//10 10
	p.set(1, 2);
	cout << "p is already intialized" << endl;
	p.Show();//1 2
	Fun3(p);//使用对象引用作为函数参数 10 10
	cout << "p is changed in main:" << endl;
	p.Show();//10 10
	return 0;
}

1.6 类的静态成员

静态成员是指声明为static的成员,在类的范围内所有对象共享该数据

静态成员可说明为共有、私有的或保护的。

若为共有的可直接访问,引用静态成员的格式为:

<类名>::<静态成员>
对象名.共有静态成员
对象指针->静态成员

静态成员可分为:

  1. 静态数据成员
  2. 静态成员函数

1.6.1 静态数据成员

静态数据成员不属于任何对象,它在程序编译时创建并且初始化,所有在该类的任何对象被创建前就存在。

静态数据成员初始化的格式为:

<数据类型><类名>::<静态数据成员名> = <初始值>;

1.6.2 静态成员函数

静态成员函数的定义是在一般函数定义前加上static关键字。

(1)一般情况下,静态成员函数主要用来访问全局变量同一个类中的静态数据成员。可以用在建立任何对象之前处理静态数据成员。

(2)静态成员函数不访问类中的非静态成员

(3)静态成员函数中是没有this指针的。

(4)静态成员函数可以在类体内定义,也可以在类外定义,在类外定义时,不用static前缀。

class Student
{
public:
	static int count;//班级人数 静态数据成员
	static double total;//班级的总分 静态数据成员
	Student(double score) {
		this->score = score; 
		count++;
		total += score;
	}
	static double getAverage() {
        //cout<<score<<endl;//error 静态成员函数不访问类中的非静态成员
		return total / count;
	}
	static double getTotal() {
		return total;
	}
private:
	double score;//个人的分数
};
int Student::count = 0;//static成员初始化、
double Student::total = 0;
int main() {
	/*cout << Student::count << endl;//0
	cout << Student::total << endl;//0
	Student s1(70);
	cout << Student::count << endl;//1
	cout << Student::total << endl;//70
	cout << s1.count << endl;//1
	*/
	Student s[3] = { Student(70),Student(80),Student(90) };
	//cout << s1.getAverage() << endl;//80
	cout << Student::getAverage() << endl;//80
	cout << Student::getTotal() << endl;//240
	//cout << s[0].count << endl;//3
	cout << Student::count << endl;//3
	return 0;
}

1.7 类的友元

(1)使用友元函数的目的是提高程序的运行效率

(2)慎用友元函数,因为它可以在类外直接访问类的私有或保护成员,破坏了类信息隐蔽的特性

1.7.1 友元函数

  1. 友元函数是在类中说明的由关键字friend修饰的非成员函数。

  2. 友元函数不是当前类的成员函数,而是独立于当前类的外部函数,但它可以访问该类的所有对象的成员。

  3. 在类中声明友元函数,其原型为:friend <类型><友元函数>(<参数表>);此声明可以放在共有部分,也可以放在保护部分和私有部分。

  4. 友元函数可以定义在类内部,也可以定义在类外部。

    class Point {
    public:
    	Point(int a = 0, int b = 0)//带缺省参数的构造函数
    	{
    		x = a;
    		y = b;
    	}
    	void Show()//用于输出
    	{
    		cout << "x=" << x << " y=" << endl;
    	}
    	int getX () const //对于一个常量对象,只能调用常函数
    	{
    		return x;
    	}
    	const int getY() const
    	{
    		return y;
    	}
    	friend double Distance(const Point& p1, const Point& p2);//普通函数作为友元函数
    private:
    	int x;
    	int y;
    };
    /*
    double Distance(const Point& p1, const Point& p2)
    {
    	double d;
    	//d = sqrt((p2.x - p1.x)(p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)); //error 不能访问私有成员
    	d = sqrt((p2.getX() - p1.getX())*(p2.getX() - p1.getX()) + (p2.getY() - p1.getY()) * (p2.getY() - p1.getY()));
    	return d;
    }
    */
    double Distance(const Point& p1, const Point& p2)
    {
    	double d;
    	d = sqrt((p2.x - p1.x)*(p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y));
    	return d;
    }
    int main() {
    	Point p1, p2(1, 1);//定义了Point的两个对象p1和p2
    	//调用Distance计算p1和p2间的距离
    	cout << Distance(p1, p2);//1.41421
    	return 0;
    }
    

1.7.2 友元成员

  1. 一个类的成员函数也可以作为另一个类的友元
  2. 这种成员函数不仅可以访问自己所在类对象中的所有成员,还可以访问friend声明语句所在类对象中的所有成员
  3. 这样能使两个类相互合作、协调工作,完成某一任务

1.7.3友元类

  1. 友元还可以是类,即一个类可以作为另一个类的友元。

  2. 当一个类作为另一个类的友元时,则该类中所有成员函数都是另一个类的友元成员,都可以访问另一个类的所有成员

  3. 友元类的声明格式:friend 类名;

    此语句可以放在共有部分,也可以放在私有部分或保护部分。

class Girl;//声明
class Boy {
	string name;
public:
	Boy(string nm) {
		name = nm;
	}
	void IntroOneself() {
		cout << "I am " << name << endl;
	}
	void IntroFriend1(Girl &x);//必须事先声明Girl
	void IntroFriend2(Girl& x);
};
class Girl {
	string name;
	friend Boy;//声明类Girl为类Boy的友元类
public:
	Girl(string nm) {
		name = nm;
	}
	void IntroOneself() {
		cout << "I am " << name << endl;
	}
	//friend void Boy::IntroFriend1(Girl &x);
	//声明Boy的成员函数IntroFriend()是Girl的友元函数
};
void Boy::IntroFriend1(Girl &x) {
	cout << "She is " << x.name << endl;
	//介绍自己的女朋友
}
void Boy::IntroFriend2(Girl& x) {
	cout << x.name<<" is my best friend." << endl;
	//介绍自己的女朋友
}
int main() {
	Boy b("Tom");
	Girl g("Mary");
	b.IntroOneself();// I am Tom
	b.IntroFriend1(g);//She is Mary
	b.IntroFriend2(g);//She is Mary
	return 0;
}

说明

  1. 友元关系是单向的,不具有交换性,即类A中将类B声明为自己的友元类,但类B中没有将类A声明为友元类,所有类A的成员函数不可以访问类B的私有成员。
  2. 当两个类都将对方声明为自己的友元时,才可以实现互访。
  3. 友元关系也不具备传递性,即类A将类B声明为友元,类B将类C声明为友元,此时,类C不一定是类A的友元。

1.3.8 对象成员

如果一个类的对象是另一个类的数据成员,则称这样的数据成员为对象成员。例如:

class A{
//...
};
class B{
A a;//对象成员
...
};
对象成员初始化问题
class X{
	类名1 对象成员名1;
	类名2 对象成员名2;
	类名n 对象成员名3}

一般来说,类X的构造函数的定义形式为:

X:X(形参表0):对象成员1(参数表1),对象成员名2(参数表2),..对象成员名n(参数表n)
{//构造函数体}
构造函数的调用顺序为:

对象成员所属类的构造函数、本类的构造函数如果对象成员不止一个,则按各对象成员在类声明中的顺序依次调用它们的构造函数,使这些对象初始化。

析构函数的调用顺为:

始终与构造函数的调用顺序正好相反,即先调用本类的析构函数,再调用对象成员所在类的析构函数。

class Date//Date类的定义
{
public:
	Date(int y, int m, int d)//构造函数
	{
		cout << "Constructing Date" << endl;
		year = y;
		month = m; 
		day = d;
	}
	void Show()//输出函数
	{
		cout << year << "." << month << "." << day << endl;
	}
	~Date() {
		cout << "Destructing Date" << endl;
	}
private:
	int year, month, day;
};
class Time//Time的定义
{
public:
	Time(int h, int m, int s)//构造函数
	{
		cout << "Constructing Time" << endl;
		hour = h;
		minute = m;
		second = s;
	}
	void Show() {
		cout << hour << ":" << minute << ":" << second << endl;
	}
	~Time() {
		cout << "Destructing Time" << endl;
	}
private:
	int hour, minute, second;
};
class Schedule//Schedule的定义
{
public:
	Schedule(int num,int y, int m, int d, int a, int b, int c,string w):date(y,m,d),time(a,b,c)
	{
		number = num;
		work = w;
		cout << "Constructing Schedule" << endl;
	}
	void Show() {
		cout << number << endl;
		date.Show();
		time.Show();
		cout << work << endl;
	}
	~Schedule() {
		cout << "Destructing Schedule" << endl;
	}
private:
	int number;
	Date date;//定义对象成员 date
	Time time;//定义对象成员 time
	string work;
};
int main() {
	Schedule s(1, 2016, 7, 27, 15, 11, 11, "开会");
	s.Show();
	/*Constructing Date
		Constructing Time
		Constructing Schedule
		1
		2016.7.27
		15:11 : 11
		开会
		Destructing Schedule
		Destructing Time
		Destructing Date
	*/
	return 0;
}

1.9 常对象

对于既需要共享,又需要防止改变的数据,应该声明为常量进行保护,因为常量在程序运行期间是不可以改变的。这些常量需要用关键字const来定义

  • 常数据成员————const修饰数据成员
  • 常数据函数————const修饰成员函数
  • 常对象————const修饰类的对象

1.9.1常数据成员——类的数据成员用const说明

(1)如果类中说明了常数据成员,则构造函数只能通过初始化列表对该数据成员进行初始化。

(2)其它函数都不能对常数据成员进行修改,只能访问

1.9.2 常成员函数——成员函数用const说明

常成员函数的声明格式为:

类型 函数名(参数表) const;

(1)在常成员函数的原型声明及函数定义的首部都要使用关键字const。

(2)常成员函数不能修改本类的数据成员,也不能调用普通的成员函数,从而保证了在常成员函数中不会 修改数据成员的值。

(3)关键字const可以作为函数重载的标志

(4)访问属性为public的常成员函数可以通过该类的任何对象调用。

1.9.3 常对象——对象用const修饰

常对象函数的声明格式

const 类名 对象名; 或 类名 const 对象名

(1)常对象必须初始化,而且不能被更新。

(2)由于常对象的值(包括所有的数据成员的值)不能被改变,因此,通过常对象只能调用常对象函数,而不能调用类中的其他普通成员函数。

eg.要求姓名一旦初始化就不能再修改

class Student {
public:
	Student(string n, string i):name(n)//name是常数据成员,只能采用成员列表初始化
	{
	 //	name = n; error
		id = i;
	}
	  void showStudent() {//输出函数
		  cout << "name:\t" << name << "\tid:\t" <<id<< endl;
		  cout << "no const showStudent" << endl;
	  }
	  void showStudent() const{//常成员函数
		  //modifyStudent(); error
		  //id="f11118; error s2是常对象,一旦初始化好name,id,便不可再更改
		  //name="Nacy" error name本身也是常数据成员 且 常对象的数据不可更改
		  cout << "name:\t" << name << "\tid:\t" << id << endl;
		  cout << "const showStudent" << endl;
	  }
	  void modifyStudent() {
		  id = "f11112";//可以
	  }
	  string getName() {
		  return name;
	  }
private:
	const string name;//常数据成员
	string id;
};
int main() {
	Student s1("simon", "f11111");//s1是普通对象
	s1.showStudent();//name:  simon   id:   f11111
	s1.modifyStudent();
	s1.showStudent();
	//name:  simon   id:   f11112
	//no const showStudent
	const Student s2("Mary","f11115");
	s2.showStudent(); //通过常对象只能调用常对象函数
	//name:  Mary   id:   f11115
	//"const showStudent
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值