【c++ primer plus】第十章-对象和类

过程性编程和面向对象编程

oop特性:抽象-封装和数据隐藏-多态-继承-代码的可重用性

从用户的角度思考问题。

抽象和类

类:类是具有相同属性和方法的一类对象集合的抽象。它包含数据抽象(数据成员)和行为抽象(成员函数),定义类的过程就是对问题进行抽象和封装的过程。

成员函数

可以放在类中,也可以放在类外。

外:必须在类体内进行原型声明

#include<iostream>
using namespace std;
class point
{
private:
	int x, y;
public:
	void set1(int a, int b)
	{
		x = a;
		y = b;
	}
	void show();
};
void point::show()//类放在函数体外
	{
		cout << "(" << x << "," << y << ")";
	}
int main()
{
	point p1;
	p1.set1(1,1);
	p1.show();
	system("pause");
	return 0;
}

内:定义的函数被默认为内联函数

对象

对象是类的实列或者是实体,一般格式为:

        <类名><对象名表>;

对象成员访问方式

  • 圆点访问方式

对象名.成员名  或指针对象的指针.成员名

  • 指针访问方式

对象指针变量名->成员名  或(&对象名)->成员名

point* point_p1;
	point_p1 = &p1;
	point_p1->set1(2, 2);
	p1.show();
	(*point_p1).set1(3, 3);
	(&p1)->show();

对象数组的创建

point p[3];
	for (int i = 0; i < 3; i++)
	{
		p[i].set1(i,i);
		p[i].show();
	}

类的界面和实现

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

  • 类的界面
  • 类的实现
#include "point.h"//类的实现
void point::show()
{
	cout << "(" << x << "," << y << ")";
}
void point::set1(int a, int b)
{
	x = a;
	y = b;
}
#include<iostream>//类的界面
using namespace std;
class point
{
private:
	int x, y;
public:
	void set1(int a, int b);
	void show();
};
#include<iostream>//主函数
using namespace std;
#include "point.h"
int main()
{
	point p1;
	p1.set1(1,1);
	p1.show();

	point* point_p1;
	point_p1 = &p1;

	point_p1->set1(2, 2);
	p1.show();
	(*point_p1).set1(3, 3);
	(&p1)->show();

	//定义对象数组
	point p[3];
	for (int i = 0; i < 3; i++)
	{
		p[i].set1(i,i);
		p[i].show();
	}
	system("pause");
	return 0;
}

注:目的是有利于修改和为同一个界面提供不同的内部实现。

构造函数

构造函数特点

是一种特殊的成员函数,对象的创建和初始化都是由1它来完成的,格式

<类名>::<类名>(<形参表>)

{

        <函数体>
}

特点

  • 被声明为公有
  • 函数名与类名相同
  • 可以重载
  • 不能指定返回类型
  • 不能显示被调用

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

<类名>::<类名>()

{
}

#include<iostream>
using namespace std;
#include"point.h"
int main()
{
	point p1(2);//不带参数则调用不带参数的构造函数,带参数则调用带参数的构造函数,2给a
	//p1.set1(1,1);
	p1.show();
	point p,q(1,1);//带两个参数的构造函数和不带
	point f[3];//创建三次调用,记得函数名是不一样的
	system("pause");
	return 0;
}//创建时一定要有对应参数的构造函数
#include "point.h"//类的实现
#include<iostream>
using namespace std;
point::point()//系统生成的构造函数
{
	cout << "我是不带参数的构造函数" << endl;
}
point::point(int a)//验证函数重载,创建带两个参数的构造函数
{
	x = a;
	y = 18;
	cout << "带一个参数的构造函数" << endl;
}
point::point(int a,int b)//验证函数重载,创建带两个参数的构造函数
{
	x = a;
	y = b;
	cout << "带两个参数的构造函数" << endl;
}
point::~point()
{

}
void point::show()
{
	cout << "(" << x << "," << y << ")";
}
void point::set1(int a, int b)
{
	x = a;
	y = b;
	//cout << "我是带两个参数的构造函数" << endl;
}
#include<iostream>//类的界面
using namespace std;
class point
{
private:
	int x, y;
public:
	void set1(int a, int b);
	void show();
	point(int a, int b);
	point(int a);
	point();//系统自动添加的构造函数,前面不可以有返回类型
	virtual ~point();//析构函数
};

成员初始化表

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

类名::构造函数名(【参数表】)【:(成员初始化表)】

{

        //构造函数体
}

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

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

#include<iostream>
using namespace std;
class sample
{
	int x;//数据成员
	int& rx;//引用一般直接赋初值。但是在类当中不可以赋值,只能有数据成员和成员函数的定义
	const float pi;
public:
	sample(int x1):x(x1),rx(x),pi(3.14f)//初始化表赋值,创建对象后立即执行的一种成员函数
	{
		//x = x1;
	}
	void print()
	{
		cout << "x=" << x << rx << pi << endl;
	}
};
int main()
{
	sample s(10);
	s.print();
	int a = 9;
	int& b = a;
	const float pi = 3.14f;//长变量的定义也是立马进行初始化
	system("pause");
	return 0;
}

具有默认参数的构造函数

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

#include<iostream>//默认参数的构造函数
using namespace std;
class point
{
	int x, y;
public:
	point(int a = 8, int b = 8)
	{
		x = a;
		y = b;
		cout << "我是构造函数,调用" << endl;
	}
	void show()
	{
		cout << x << "," << y << endl;
	}
};
int main()
{
	point p1;
	p1.show();
	point p2(5,6);
	p2.show();
	point p3(1);
	p3.show();
	system("pause");
	return 0;
}

析构函数

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

➢只能被声明为公有函数。

➢析构函数的名字同类名,与构造函数名的区别在于析构函数名前加~,表明它的功能与构造函数的功能相反。

➢析构函数没有参数,不能重载,一个类中只能定义一个析构函数。

➢不能指定 返回类型。

➢析构函数在释放一个对象时侯被自动调用。

默认析构函数


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

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

        
}
 

#include<iostream>//析构函数的主要目的是释放空间
using namespace std;
class point
{
	int x, y;
public:
	point()
	{
		cout << "构造函数调用" << endl;
		cin >> x >> y;
	}
	point(int a, int b)
	{
		x = a;
		y = b;
		cout << "我是构造函数2" << endl;
	}
	void show()
	{
		cout << x << y << endl;
	}
	~point()
	{
		cout << "我是析构函数" << x << " " << y << endl;
	}
};
int main()
{
	point* p = new point(1, 1);//创建一个对象
	cout << "999" << endl;
	delete p;//释放一个对象,释放同时调用析构函数进行输出
	cout << "kkkk" << endl;
	system("pause");
	return 0;
}

析构函数的用途:

#include<iostream>
using namespace std;
class point
{
	int* p;
public:
	point()//构造空间
	{
		p = new int;//开辟空间
		*p = 3;
		cout << "我是构造函数,调用我" << endl;
	}
	void show()//用于输出
	{
		cout << *p << endl;
	}
	~point()//析构函数
	{
		cout << "我是析构函数" << endl;
		delete p;//释放空间,如果开辟运行结束后程序种没有添加析构函数,则系统自动添加一个空的析构函数数,但是不会把空间释放掉
	}
};
int main()
{
	point p;
	p.show();
	system("pause");
	return 0;
}

 拷贝构造函数

1.拷贝构造函数的定义
拷贝构造函数是- -种特殊的构造函数,它的作用是用一个已经存在的对象去初始化另-一个对象。
其格式为:
<类名>: : <类名> (const <类名>&<对象名>
{
        <函数体>
}

2.拷贝构造函数的特点

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

3. 在以下3种情况下会被自动调用:
➢当用类的一个对象去初始化该类的另一个对象时

➢当函数的形参是类的对象,进行形参和实参结合时

➢当函数的返回值是类的对象,函数执行完成返回调用者时
 

#include<iostream>
using namespace std;
class point
{
	int x, y;
public:
	point(int a, int b)
	{
		x = a;
		y = b;
		cout << "constructing.." << endl;
	}
	point(const point& p)
	{
		x = p.x;
		y = p.y;
		cout << "copy constructing.." << endl;
	}
	~point()
	{
		cout << "destructing.." << endl;
	}
	void show()
	{
		cout << x << "," << y << endl;
	}
};
point fun(point temp)//调用两个拷贝函数,系统会自动创建一个没有名字的对象,不然p无法返回给p4,所以再调用一次拷贝函数
{
	point p(temp);
	return p;
}
int main()
{
	point p1(1, 1);
	point p2(2, 2);
	point p3(p1);
	point p4 = p2;//调用拷贝构造函数
	p4 = p1;//p4已经存在,所以是对p4进行赋值,不会调用拷贝,初始化才调用
	p4 = fun(p2);
	system("pause");
	return 0;
}

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

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

        如: Coord p2(p1); // 用对象p1初始化对象p2,拷贝构造函数被调用(代入法)

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

  • 当函数的形参是类的对象,调用函数,进行形参和实参结合时。例如
fun1(coord p)//函数的形参是类的对象
{
    p.print();
}
main()
{
    coord p1(10,20);
    fun1(p1);//当调用函数,进行形参和实参结合时
    return 0;
}
  • 当函数的返回值是一个对象,函数执行完成返回调用者时,例如:
coord fun2()
{
    coord p1(10,30);
    return p1;//函数的返回值是对象
}
main()
{
    coord p2;
    p2=fun2();//函数执行完返回调用者时
    return 0;
}

浅拷贝与深拷贝

默认拷贝构造函数

如果一个类中没有定义拷贝构造函数,则系统自动生成一个默认的拷贝构造函数。如Point类中,默认的拷贝构造函数为:
Point(const Point &p)
{
        X=p.X;
        y= p.y;
}

#include<iostream>//默认参数的构造函数
using namespace std;
class point
{
	int x, y;
public:
	point(int a = 8, int b = 8)
	{
		x = a;
		y = b;
		cout << "我是构造函数,调用" << endl;
	}
	void show()
	{
		cout << x << "," << y << endl;
	}
};
int main()
{
	point p1;
	p1.show();
	point p2(5,6);
	p2.show();
	point p3(1);
	p3.show();
	system("pause");
	return 0;
}

浅拷贝

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

深拷贝

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

#include<iostream>
using namespace std;
class point
{
	int x, y;
public:
	point(int a, int b)
	{
		x = a;
		y = b;
		cout << "constructing.." << endl;
	}
	point(const point& p)
	{
		x = p.x;
		y = p.y;
		cout << "copy constructing.." << endl;
	}
	~point()
	{
		cout << "destructing.." << endl;
	}
	void show()
	{
		cout << x << "," << y << endl;
	}
};
point fun(point temp)//调用两个拷贝函数,系统会自动创建一个没有名字的对象,不然p无法返回给p4,所以再调用一次拷贝函数
{
	point p(temp);
	return p;
}
int main()
{
	point p1(1, 1);
	point p2(2, 2);
	point p3(p1);
	point p4 = p2;//调用拷贝构造函数
	p4 = p1;//p4已经存在,所以是对p4进行赋值,不会调用拷贝,初始化才调用
	p4 = fun(p2);
	system("pause");
	return 0;
}

this指针

#include<iostream>
using namespace std;
class date
{
private:
	int year, month, day;
public:
	date(int y = 2009, int n = 1, int d = 1)
	{
		year = y;
		month = n;
		day = d;
	}
	date& fun()
	{
		return *this;
	}
	void show()//const date *this=&d1;
	{
		cout << year << "." << month << "." << day << endl;
		//this->year.....
	}
};
int main()
{
	date d1(1993, 3, 20), d2(2000, 1, 2);
	d1.show();//只访问d1而没有访问d2是因为this指针
	cout << sizeof(int) << endl;
	cout << sizeof(date) << endl;//占12个字节因为有三个成员函数
	(d1.fun()).show();;//类需要返回当前对象的引用
	system("pause");
	return 0;
}
  • this指针是由C++编译器自动产生且较常用的一个隐含对象指针,它不能被显式声明。
  • this指针是一个局部变量,局部于某个对象。
  • this指针是一个const指针,不能修改它或给它赋值。

this指针的用途:

  • 为了区分成员和非成员
  • 类的方法需要返回当前对象的引用

向函数传递对象

  • 使用对象作为函数参数
  • 使用对象指针作为函数参数
  • 使用对象引用作为函数参数
#include<iostream>
using namespace std;
class point
{
	int x, y;
public:
	point(int a, int b)
	{
		x = a;
		y = b;
	}
	point(const point& p)//拷贝构造函数
	{
		x = p.x;
		y = p.y;
		cout << "copy constructor" << endl;
	}
	void set(int a, int b)
	{
		x = a;
		y = b;
	}
	void show()
	{
		cout << "(" << x << "," << y << ")" << endl;
	}
};
//void fun(point point)//使用对象作为函数参数
//{
//	point.set(10, 10);
//	cout << "point's x and y are:" << endl;
//	point.show();
//}
//void fun(point* point)
//{
//	point->set(10, 10);
//	cout << "point's x and y are:" << endl;
//	point->show();
//}
void fun(const point &point)//传引用,加const则值不能被修改
{
	point.set(10, 10);
	cout << "point's x and y are:" << endl;
	point.show();
}
int main()
{
	point p(1, 1);
	//fun(p);传对象
	//fun(&p);指针
	fun(p);
	cout << "but,p is unchanged in main:" << endl;
	p.show();
	system("pause");
	return 0;
}

类的静态成员

静态成员是指声明为static的成员,在类的范围内所有对象共享该数据。
静态成员可说明为公有的、私有的或保护的。
若为公有的可直接访问,引用静态成员的格式为:
➢<类名>::<静态成员>

➢对象名.公有静态成员

➢对象指针->静态成员

静态数据成员

静态数据成员不属于任何对象,它在程序编译时创建并且初始化,所以在该类的任何对象被创建前就存在。
静态数据成员初始化的格式为:
<数据类型> <类名>::<静态数据成员名> = <初始值>;

静态成员函数的定义:是在一般函数定义前加上static关键字。
(1)一般情况下,静态成员函数主要用来访问全局变量或同一个类中的静态数据成员。可以用在建立任何对象之前处理静态数据成员。
(2)静态成员函数不访问类中的非静态成员。
(3)静态成员函数中是没有thi s指针的。
(4)静态成员函数可以在类体内定义,也可以在类外定义,在类外定义时,不用static前缀。

#include<iostream>
using namespace std;
class student
{
public:
	static int count;
	static double total;//两个静态数据成员的定义
	student(double score)
	{
		this->score = score;
		count++;
		total += score;
	}
	static double getaverage()
	{
		return total / count;
	}
	static double gettotal()
	{
		return total;
	}
private:
	double score;//个人分数
};
int student::count = 0;
double student::total = 0;//两个静态数据成员的初始化
int main()
{
	student s[3] = { student(70),student(70) ,student(70) };//创建一个数组存放成员
	/*cout << student::count << endl;
	cout << student::total << endl;
	student s1(70);
	cout << student::count << endl;
	cout << student::total << endl;
	cout << s1.count << endl;*/
	//student s1(70),s2(80);
	//cout << s1.getaverage() << endl;
	//cout << student::getaverage();//将get函数定义为static成员则可以这样显示结果
	//cout << student::gettotal();
	cout << student::count<<endl;//则可以计算出数组当中有三个数
	system("pause");
	return 0;
}

对象成员

如果一个类的对象是另一个类的数据成员,则称这样的数据成员为对象成员。例如:
class A{//... };
class B
{A  a; //对象成员.....};

对象成员的初始化问题

class X

{
        类名1对象成员名1;
        类名2对象成员名2;
        类名n对象成员 名n;
}
一般来说,类X的构造函数的定义形式为:
x::x (形参表0) :对象成员1(参数表1),对象成员名2 (参数表2),..,对象成员名n(参数表n)
{

 //构造函数体

}

构造函数的调用顺序为:
        对象成员所属类的构造函数、本类构造函数,如果对象成员不只一一个,则按各对象成员在类声明中的顺序依次调用它们的构造函数,对这些对象初始化。
析构函数的调用顺序:
        始终与构造函数的调用顺序正好相反,即先调用本类的析构函数,再调用对象成员所在类的析构函数。

对象成员的使用方法:

#include<iostream>//对象成员的用法
#include<string>
using namespace std;


class 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
{
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
{
public:
	schedule(int n, int a, int b, int c, int d, int e, int f,string w):date(a,b,c),time(d,e,f)
	{
		number = n;//初始化
		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;
	Time time;//对象成员
	string work;
};
int main()
{
	schedule s(1,2010,7,27,15,11,11,"开会");
	s.show();
	system("pause");
	return 0;
}

const的使用方法

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

  • 如果类中说明了常数据成员,则构造函数只能通过初始化列表对该数据成员进行初始化。
  • 其它函数都不能对常数据成员进行修改,只能访问。

➢常成员函数------ const修饰成员函数
常成员函数的声明格式为:
类型    函数名(参数表)   const;

  • 在常成员函数的原型声明及函数定义的首部都要使用关键字const。
  • 常成员函数不能修改本类的数据成员,也不能调用普通的成员函数,从而保证了在常成员函数中不会修改数据成员的值。
  • 关键字const可以作为函数重载的标志。
  • 访问属性为public的常成员函数可以通过该类的任何对象调用。

➢常对象------ const修饰类的对象

常对象的定义格式为:
const  类名  对象名;  或  类名 const  对象名;

  • 常对象必须进行初始化,而且不能被更新。
  • 由于常对象的值( 包括所有的数据成员的值)不能被改变,因此,通过常对象只能调用常成员函数,而不能调用类中的其他普通成员函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值