c++面向对象基础知识回顾

基础知识

I/O格式控制

setprecision(int) 设置浮点数小数位数(包括小数点)
setw(int) 设置域宽

//cout的格式控制
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
	float f = 3.1254;
	cout << setw(10) << f << endl;
	cout << setprecision(3) << f << endl;
	return 0;
}

输出:

    3.1254
3.13

交换函数:

#include<iostream>
using namespace std;
void swap(int &a, int &b) {
	int t = a;
	a = b;
	b = t;
}
int main() {
	int x = 5, y = 10;
	cout << "x =" << x << " y =" << y << endl;
	swap(x, y);
	cout << "x =" << x << " y =" << y << endl;
	return 0;
}

输出:

x =5 y =10
x =10 y =5

内联函数

  • 声明时使用关键字 inline
  • 编译时在调用处用函数体进行替换,节省了参数传递、控制转移等开销
#include <iostream>
using namespace std;
const double PI = 3.1415926;
inline double calArea(double radius) {
	return PI * radius * radius;
}
int main() {
	double r = 3.0;
	double area = calArea(r);
	cout << area << endl;
	return 0;
}

输出:

28.2743

带默认参数值的函数

  • 函数在定义形参时可预先给出默认值,调用时如给出实参,则采用实参值,否则采用预先给出的默认参数值
  • 有默认参数的形参必须在形参列表的最后,也就是说默认参数值的右面不能有无默认值的参数(因为调用时实参与形参的结合是从左向右的顺序)
  • 如果一个函数有原型声明,且原型声明在定义之前,则默认参数值必须在函数原型声明中给出
#include <iostream>
using namespace std;
//原型声明在前
int add(int x = 5,int y = 6);

int main() {
	int k=add();
	cout<<k<<endl;
}

//此处不能再指定默认值
int add(int x,int y) {
	return x * y;
}

输出:

30
  • 而如果只有函数的定义,或函数定义在前,则默认参数值需在函数定义中给出
int add(int x=5,int y=6){
	//只有定义,没有原型声明
	return x + y;
}
int main() {
	add();
}

计算长方体的体积:

#include <iostream>
#include <iomanip>
using namespace std;
int getVolume(int length, int width = 2, int height = 3);
int main() {
	const int X = 10, Y = 12, Z = 15;
	cout << "Some box data is" ;
	cout << "体积:"<<getVolume(X, Y, Z) << endl;
	cout << "Some box data is" ;
	cout << "体积:"<<getVolume(X, Y) << endl;
	cout << "Some box data is" ;
	cout << "体积:"<<getVolume(X) << endl;
	return 0;
}
//此处不能再指定默认值
int getVolume(int length, int width, int height){
	cout<<setw(5)<<length<<setw(5)<<width<<setw(5)<<height;
	return length * width * height;
}

结果:

Some box data is   10   12   15体积:1800
Some box data is   10   12    3体积:360
Some box data is   10    2    3体积:60

函数重载

  • C++允许功能相近的函数在相同的作用域内以相同函数名声明,从而形成重载,方便使用,便于记忆
  • 重载函数的形参必须不同:个数不同或类型不同
  • 编译程序将根据实参和形参的类型及个数的最佳匹配来选择调用哪一个函数(不以形参名返回值来区分)
//形参类型不同
int add(int x, int y);
float add(float x, float y);

//形参个数不同
int add(int x, int y);
int add(int x, int y, int z);

编写两个名为sumOfSquare的重载函数,分别求两整数的平方和及两实数的平方和:

#include <iostream>
using namespace std;
int sumOfSquare(int a, int b) {
	return a * a + b * b;
}
double sumOfSquare(double a, double b) {
	return a * a + b * b;
}
int main() {
	int m, n;
	cout << "Enter two integer: ";
	cin >> m >> n;
	cout << sumOfSquare(m, n) << endl;
	double x, y;
	cout << "Enter two real number: ";
	cin >> x >> y;
	cout << sumOfSquare(x, y) << endl;
	return 0;
}
/*
3 5
3.5 2
*/

输出:

Enter two integer: 3 5
34
Enter two real number: 3.5 2
16.25

类与对象

面向对象的4个特征:

抽象,封装,继承,多态

class 类名称
{
	public:
		公有成员(外部接口)
	private:
		私有成员
	protected:
		保护型成员
}

例子:

#include<iostream>
using namespace std;
class Clock{
	public:
		void setTime(int h = 0, int m = 0, int s = 0);
		void showTime();
	private:
		int hour, minute, second;
};
int main() {
	Clock myClock;
	myClock.setTime(8, 30, 30);
	myClock.showTime();
	return 0;
}
void Clock::setTime(int h, int m, int s){
	hour = h;
	minute = m;
	second = s;
}
void Clock::showTime(){
	cout<<hour<<":"<<minute<<":"<< second;
}

输出:

8:30:30

构造函数

  • 是和类名相同的成员函数(函数名字和类名相同)
  • 没有返回值,void也不可以
  • 必须是public
  • 自动调用
  • 构造函数可以是内联函数、重载函数、带默认参数值的函数

默认构造函数

  • 无参数的构造函数都是默认构造函数
  • 全部参数都有默认值的也是默认构造函数
  • 当不定义构造函数时,编译器自动产生默认构造函数,无参数
  • 可以自定义默认构造函数
下面两个都是默认构造函数,如果在类中同时出现,将产生编译错误:
Clock();
Clock(int h=0,int m=0,int s=0)

改一下之前的代码:

#include<iostream>
using namespace std;
class Clock{
	public:
		Clock(int h,int m,int s);//构造函数
		void setTime(int h = 0, int m = 0, int s = 0);
		void showTime();
	private:
		int hour, minute, second;
};
int main() {
	//定义对象时自动调用构造函数
	Clock c1(0,0,0),c2(12,12,12);
	
	c1.showTime();
	c2.showTime();
	return 0;
}
//2种方式均可
//Clock::Clock(int h, int m, int s): hour(h),minute(m),second(s){}
Clock::Clock(int h, int m, int s){
	hour=h;
	minute=m;
	second=s;
}
void Clock::setTime(int h, int m, int s){
	hour = h;
	minute = m;
	second = s;
}
void Clock::showTime(){
	cout<<hour<<":"<<minute<<":"<<second<<endl;
}

输出:

0:0:0
12:12:12

复制构造函数

  • 复制构造函数是一种特殊的构造函数,其形参为本类的对象引用,作用是用一个已存在的对象去初始化同类型的新对象
#include <iostream>
using namespace std;
class Point { //Point 类的定义
	public:
		Point(int xx, int yy){ x = xx; y = yy; } //构造函数,内联
		Point(Point& p); //复制构造函数
	private:
		int x, y; //私有数据
};
Point::Point (Point& p) {
	x = p.x; y = p.y;
	cout << "Calling the copy constructor " << endl;
}
int main() {
	Point a(4, 5);
	Point b = a; //调用复制构造函数
	Point c(a); //调用复制构造函数
}

输出:

Calling the copy constructor
Calling the copy constructor

例一:

#include <iostream>
using namespace std;
class Point { //Point 类的定义
	public:
		Point(int xx, int yy){ x = xx; y = yy; } //构造函数,内联
//		Point(Point& p); //复制构造函数
		int getX(){
			return x;
		}
	private:
		int x, y; //私有数据
};
//Point::Point (Point& p) {
//	x = p.x; y = p.y;
//	cout << "Calling the copy constructor " << endl;
//}
void fun1(Point p) {
	cout << p.getX() << endl;
}
Point fun2() {
	Point a(1, 2);
	return a;
}
int main() {
	Point a(4, 5);
	fun1(a); 
}

输出:

4

例二:

#include <iostream>
using namespace std;
class Point { //Point 类的定义
	public:
		Point(int xx, int yy){ x = xx; y = yy; } //构造函数,内联
		Point(Point& p); //复制构造函数
		int getX(){
			return x;
		}
	private:
		int x, y; //私有数据
};
Point::Point (Point& p) {
	x = p.x; y = p.y;
	cout << "Calling the copy constructor " << endl;
}
void fun1(Point p) {
	cout << p.getX() << endl;
}
Point fun2() {
	Point a(1, 2);
	return a;
}
int main() {
	Point a(4, 5);//情况二,对象a作为fun1的实参
	fun1(a); 
}

输出:

Calling the copy constructor
4

解释:

  • 如果函数的形参是类的对象,调用函数时,将使用实参对象初始化形参对象,发生复制构造
  • 如果程序员没有为类声明复制构造函数,则编译器自己生成一个隐含的复制构造函数
#include <iostream>
using namespace std;
class Point { //Point 类的定义
	public:
		Point(int xx, int yy){ x = xx; y = yy; } //构造函数,内联
		int get(){
			return x;
		}
	private:
		int x, y; //私有数据
};
Point fun2() {
	Point a(1, 2);
	return a;
}
int main() {
	Point a = fun2();//情况三,函数的返回值是类对象
	cout<<a.get();
	return 0;
}

结果:

1

小疑惑:

不知道为啥会报错QAQ

#include <iostream>
using namespace std;
class Point { //Point 类的定义
	public:
		Point(int xx, int yy){ x = xx; y = yy; } //构造函数,内联
		Point(Point& p); //复制构造函数
		int get(){
			return x;
		}
	private:
		int x, y; //私有数据
};
Point::Point (Point& p) {
	x = p.x; y = p.y;
	cout << "Calling the copy constructor " << endl;
}
Point fun2() {
	Point a(1, 2);
	return a;
}
int main() {
	Point a = fun2();//情况三,函数的返回值是类对象
	cout<<a.get();
	return 0;
}

析构函数

  • 完成对象被删除前的一些清理工作
  • 在对象的生存期结束的时刻系统自动调用它,然后再释放此对象所属的空间
  • 如果程序中未声明析构函数,编译器将自动产生一个隐含的析构函数

编译器默认提供的函数

class Empty{
	public:
		Empty(){}
		Empty(Empty& rhs){...}
		~Empty(){}
};

组合

  • 类中的成员数据是另一个类的对象
  • 可以在已有抽象的基础上实现更复杂的抽象

类组合的构造函数设计

  • 原则:不仅要负责对本类中的基本类型成员数据赋初值,也要对对象成员初始化

声明形式:

类名::类名(对象成员所需的形参,本类成员形参)
	:对象1(参数),对象2(参数),......
{
	//函数体其他语句
}

构造组合类对象时的初始化次序

  • 首先对构造函数初始化列表中列出的成员(包括基本类型成员和对象成员)进行初始化,初始化次序是成员在类体中定义的次序
    • 成员对象构造函数调用顺序:按对象成员的声明顺序,先声明者先构造
    • 初始化列表中未出现的成员对象,调用用默认构造函数(即无形参的)初始化
  • 处理完初始化列表之后,再执行构造函数的函数体

类的组合,线段(Line)类例子:

#include <iostream>
#include <cmath>
using namespace std;
class Point { //Point类定义
	public:
		Point(int xx = 0, int yy = 0) {
			x = xx;
			y = yy;
			cout<<"Calling the constructor of Point" << endl;
		}
		Point(Point &p);
		int getX() { return x; }
		int getY() { return y; }
	private:
		int x, y;
};
Point::Point(Point &p) { //复制构造函数
	x = p.x;
	y = p.y;
	cout << "Calling the copy constructor of Point" << endl;
}
//类的组合
class Line { //Line类的定义
	public: //外部接口
		Line(Point xp1, Point xp2);
		Line(Line &l);
		double getLen() { return len; }
	private: //私有数据成员
		Point p1, p2; //Point类的对象p1,p2
		double len;
};
//组合类的构造函数
Line::Line(Point xp1, Point xp2) : p1(xp1), p2(xp2) {
	double x = p1.getX() - p2.getX();
	double y = p1.getY() - p2.getY();
	len = sqrt(x * x + y * y);
	cout << "Calling the constructor of Line" << endl;
}
//组合类的复制构造函数
Line::Line (Line &l): p1(l.p1), p2(l.p2), len(l.len){
	cout << "Calling the copy constructor of Line" << endl;
}
int main() {
	cout<<"建立Point类的对象:"<<endl;
	Point myp1(1, 1), myp2(4, 5);
	cout<<"调用组合类的构造函数:"<<endl;
	Line line(myp1, myp2); 
	cout<<"利用复制构造函数建立一个新对象:"<<endl;
	Line line2(line);
	cout << "The length of the line is: ";
	cout << line.getLen() << endl;
	cout << "The length of the line2 is: ";
	cout << line2.getLen() << endl;
}

结果:

建立Point类的对象:
Calling the constructor of Point
Calling the constructor of Point
调用组合类的构造函数:
Calling the copy constructor of Point
Calling the copy constructor of Point
Calling the copy constructor of Point
Calling the copy constructor of Point
Calling the constructor of Line
利用复制构造函数建立一个新对象:
Calling the copy constructor of Point
Calling the copy constructor of Point
Calling the copy constructor of Line
The length of the line is: 5
The length of the line2 is: 5

数据的共享与保护

this指针

  • 每个对象都有一个指向自身的this指针
  • this的值是当前对象的起始地址
  • 对象调用成员函数时会将自己的this指针传递给成员函数(隐含参数)

静态数据成员

  • 用关键字static声明,为该类的所有对象共享,必须在类外定义和初始化
  • 同一类的不同对象,其成员数据之间是互相独立的,当我们将类的某一个数据成员的声明为static,则由该类所产生的所有对象的静态成员均共享一个存储空间
  • static数据成员和函数成员可以通过对象名引用也可以通过类名引用
  • static成员函数只能访问static数据成员,不能访问非static成员
  • 普通成员函数可以访问static数据成员

例子:

#include <iostream>
using namespace std;
class Point {
	public: //外部接口
		Point(int x=0,int y=0):x(x),y(y){
			count++; //所有对象共同维护同一个count
		}
		Point(Point &p) { //复制构造函数
			x = p.x;
			y = p.y;
			count++;
		}
		~Point() { count--; }
		int getX() { return x; }
		int getY() { return y; }
		void showCount() { //输出静态数据成员
			cout << " Object count =" << count << endl;
		}
	private: //私有数据成员
		int x, y;
		static int count;//静态数据成员声明,用于记录点的个数
};
int Point::count = 0;//必须定义和初始化
int main() { //主函数
	Point a(4, 5); //定义对象a,其构造函数回使count增1
	cout << "Point A: " << a.getX() << "," << a.getY();
	a.showCount(); //输出对象个数
	Point b(a); //定义对象b,其构造函数回使count增1
	cout << "Point B: " << b.getX() << "," << b.getY();
	b.showCount(); //输出对象个数
	return 0;
}

输出:

Point A: 4,5 Object count =1
Point B: 4,5 Object count =2

静态函数成员

  • 类外代码可以使用类名和作用域操作符来调用静态成员函数
  • 静态成员函数只能引用属于该类的静态数据成员或静态成员函数
#include <iostream>
using namespace std;
class Point {
	public: //外部接口
		Point(int x=0,int y=0):x(x),y(y){
			count++; //所有对象共同维护同一个count
		}
		Point(Point &p) { //复制构造函数
			x = p.x;
			y = p.y;
			count++;
		}
		~Point() { count--; }
		int getX() { return x; }
		int getY() { return y; }
		//静态成员函数来输出静态数据成员
		static void showCount() { 
			cout << " Object count =" << count << endl;
		}
	private: //私有数据成员
		int x, y;
		static int count;//静态数据成员声明,用于记录点的个数
};
int Point::count = 0;//必须定义和初始化
int main() { //主函数
	Point a(4, 5); //定义对象a,其构造函数回使count增1
	cout << "Point A: " << a.getX() << "," << a.getY();
	Point::showCount();
	Point b(a); //定义对象b,其构造函数回使count增1
	cout << "Point B: " << b.getX() << "," << b.getY();
	Point::showCount();
	return 0;
}

结果:

Point A: 4,5 Object count =1
Point B: 4,5 Object count =2

类的友元

  • 友元是C++提供的一种破坏数据封装和数据隐藏的机制
  • 通过将一个模块声明为另一个模块的友元,一个模块能够引用到另一个模块中本是被隐藏的信息
  • 可以使用友元函数和友元类

为了良好的程序设计风格,类的数据成员都应该设为私有的,但如果某个(些)外部函数需要直接访问类的数据成员,怎么办?

  • 方法一:数据公有
  • 方法二:友元

友元函数

  • 友元函数是在类声明中由friend修饰说明的非成员函数,在它的函数体中能够通过对象名访问private和protected成员
  • 作用:增加灵活性,使程序员可以在封装和隐藏性方面做合理选择

使用友元函数计算两点间的距离:

#include <iostream>
#include <cmath>
using namespace std;
class Point { //Point类声明
	public: //外部接口
		Point(int x=0,int y=0):x(x),y(y){ }
		int getX() { return x; }
		int getY() { return y; }
		friend float dist(Point &a, Point &b);
	private: //私有数据成员
		int x, y;
};
float dist( Point& a, Point& b) {
	double x=a.x-b.x;
	double y=a.y-b.y;
	return sqrt(x*x+y*y);
}
int main() {
	Point p1(1,1), p2(4,5);
	cout<<"The distance is:";
	cout<<dist(p1,p2)<<endl;
	return 0;
}

结果:

The distance is:5

共享数据的保护

  • 对于既需要共享、又需要防止改变的数据应该声明为常类型(用const进行修饰)
  • 对于不改变对象状态的成员函数应该声明为常函数

常对象:必须进行初始化,不能被更新,const 类名 对象名

常成员:用const进行修饰的类成员,包括常数据成员和常函数成员

常引用:被引用的对象不能被更新,const 类型说明符 &引用名

常对象:

class A{
	public:
		A(int i,int j) {x=i; y=j;}
		...
	private:
		int x,y;
};
A const a(3,4); //a是常对象不能被更新

常成员函数:

  • 使用const关键字说明的函数
  • 常成员函数不更新对象的数据成员
  • 格式:类型说明符 函数名(参数表)const,在实现部分也要带const关键字
  • const关键字可以被用于参与对重载函数的区分
  • 通过常对象只能调用它的常成员函数

常成员函数举例:

#include<iostream>
using namespace std;
class R{
	public:
		R(int r1,int r2):r1(r1),r2(r2){ }
		void print();
		void print() const;//
	private:
		int r1,r2;
};
void R::print(){
	cout<<r1<<":"<<r2<<endl;
}
void R::print() const{
	cout<<r1<<"::"<<r2<<endl;
}
int main() {
	R a(5,4);
	a.print(); //调用void print()
	const R b(20,52);
	b.print(); //调用void print() const
	return 0;
}

输出:

5:4
20::52

常数据成员举例:

#include <iostream>
using namespace std;
class A {
	public:
		A(int i);
		void print();
	private:
		const int a;
		static const int b; //静态常数据成员
};
const int A::b=10;
A::A(int i) : a(i) {}
void A::print() {
	cout << a << ":" << b <<endl;
}
int main() {
	A a1(100), a2(0);
	a1.print();
	a2.print();
	return 0;
}

输出:

100:10
0:10

对象数组与对象指针

对象数组初始化

  • 数组中每一个元素对象被创建时,系统都会调用类构造函数初始化该对象
  • 通过初始化列表赋值,例:Point a[2]={Point(1,2),Point(3,4)};
  • 如果没有为数组元素指定显式初始值,数组元素便使用默认值初始化(调用缺省构造函数)

其他用法详见例子:

#include <iostream>
using namespace std;
class Point { //类的定义
	public:   //外部接口
		Point();
		Point(int x, int y);
		~Point();
		void move(int newX,int newY);
		int getX() const { return x; }
		int getY() const { return y; }
		static void showCount(); //静态函数成员
	private: //私有数据成员
		int x, y;
};
Point::Point() {
	x = y = 0;
	cout << "Default Constructor called." << endl;
}
Point::Point(int x, int y) : x(x), y(y){
	cout << "Constructor called." << endl;
}
Point::~Point() { cout << "Destructor called." << endl; }

void Point::move(int newX,int newY){
	cout << "Moving the point to (" << newX << ", " << newY << ")" << endl;
	x = newX; y = newY;
}
int main() {
	Point a[2];
	cout<<"移动点:"<<endl;
	for(int i = 0; i < 2; i++)
		a[i].move(i + 10, i + 20);
	return 0;
}

结果:

Default Constructor called.
Default Constructor called.
移动点:
Moving the point to (10, 20)
Moving the point to (11, 21)
Destructor called.
Destructor called.

对象指针:

//声明形式:类名 *对象指针名
Point a(5,10);
Piont *ptr;
ptr=&a;
//通过指针访问对象成员:对象指针名->成员名
ptr->getx();

动态申请内存操作符 new:

new 类型名T (初始化参数列表)
功能:在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以初值
Point *ptr1 = new Point;
Point *ptr1 = new Point(1,2);

释放内存操作符delete:

//delete 指针p
//功能:释放指针p所指向的内存,p必须是new操作的返回值。
Point *ptr1 = new Point;
delete ptr1;
Point *ptr2 = new Point(1,2);
delete ptr1

申请和释放动态数组:

/*
分配:new 类型名T [ 数组长度 ]
数组长度可以是任何表达式,在运行时计算
释放:delete[] 数组名p
释放指针p所指向的数组,p必须是用new分配得到的数组首地址
*/
Point *ptr=new Point[2];//创建对象数组
delete[] ptr;           //删除整个对象数组

类的继承与派生

继承与派生是同一过程从不同的角度来看

  • 保持已有类的特性而构造新类的过程称为继承
  • 在已有类的基础上新增自己的特性而产生新类的过程称为派生
  • 被继承的已有类称为基类(父类),派生出的新类称为派生类(子类)
  • 一个派生类只有一个基类,是单继承,一个派生类有多个基类,则是多继承

语法见代码:

单继承时
class 派生类名:继承方式 基类名{
	成员声明;
}
继承方式:public protected private

例如:
class Point{
	private:int x,y;
};
class Circle:public Point{...};
class Cylinder:public Circle{...};

多继承时
class 派生类名:继承方式1 基类名1,继承方式2 基类名2,... {
	成员声明;
};

例如:
class Worker{...};
class Farmer{...};
class FarmerWorker:public Farmer,public Worker{...}

例子:

#include <iostream>
using namespace std;
class Shape{// 基类
   public:
      void setWidth(int w){
         width = w;
      }
      void setHeight(int h){
         height = h;
      }
   protected:
      int width;
      int height;
};

// 派生类
class Rectangle: public Shape{
   public:
      int getArea(){
         return (width * height);
      }
};

int main(void){
   Rectangle Rect;
   Rect.setWidth(5);
   Rect.setHeight(7);
   // 输出对象的面积
   cout << "Total area: " << Rect.getArea() << endl;
   return 0;
}

输出:

Total area: 35

派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private

下图为根据访问权限总结出不同的访问类型:

访问类型publicprotectedprivate
同一个类
派生类×
外部的类××

一个派生类继承了所有的基类方法,但下列情况除外:

  • 基类的构造函数、析构函数和拷贝构造函数
  • 基类的重载运算符
  • 基类的友元函数

下面讨论3种继承方式:

公有继承(public):

  • 基类的public和protected成员的访问属性在派生类中保持不变,但基类的private成员不可直接访问
  • 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员
  • 派生类的对象只能访问基类的public成员
#include <iostream>
using namespace std;
class Point { //基类Point类的定义
	public: //公有函数成员
		void initPoint(float x = 0, float y = 0){
			this->x = x; this->y = y;
		}
		void show( ){cout<<x<<","<<y<<endl; }
	private: //私有数据成员
		float x, y;
};
class Circle: public Point {//派生类定义部分
	public: //新增公有函数成员
		void initCircle(float x, float y, float r1) {
			initPoint(x, y); //调用基类公有成员函数
			this->r = r1;
		}
	private: //新增私有数据成员
		float r;
};
int main() {
	Circle c1; //定义Circle类的对象
	c1.initCircle(2, 3, 20);//设置圆的数据
	c1.show( ); //派生类对象访问基类公有成员
	return 0;
}

结果:

2,3

私有继承(private):

  • 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员
  • 通过派生类的对象不能直接访问基类中的任何成员
#include <iostream>
using namespace std;
class Point { //基类Point类的定义
	public: //公有函数成员
		void initPoint(float x = 0, float y = 0){
			this->x = x; this->y = y;
		}
		void show( ){cout<<x<<","<<y<<endl; }
	private: //私有数据成员
		float x, y;
};
class Circle: private Point {//派生类定义
	public: //新增公有函数成员
		void initCircle(float x, float y, float r1) {
			initPoint(x, y); //调用基类公有成员函数
			this->r = r1;
		}
	private: //新增私有数据成员
		float r;
};
int main() {
	Circle c1; //定义Circle类的对象
	c1.initCircle(2, 3, 20);//设置圆的数据
	//c1.show( ); //不能访问基类公有成员
	return 0;
}

结果:无

保护继承(protected):

  • 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员
  • 通过派生类的对象不能直接访问基类中的任何成员

类型转换规则:

一个公有派生类的对象在使用上可以被当作基类的对象,反之则禁止。具体表现在:

  1. 派生类的对象可以隐含转换为基类对象
  2. 派生类的对象可以初始化基类的引用
  3. 派生类的指针可以隐含转换为基类的指针。

基类对象名、指针只能访问从基类继承的成员

#include <iostream>
using namespace std;
class Base { //基类Base定义
	public:
		void display() const {
			cout << "Base::display()" << endl;
		}
};
//公有派生类Derived定义
class Derived: public Base {
	public:
		void display() const {
			cout << "Derived::display()"<<endl;
		}
};
void fun(Base *ptr) { //参数为指向基类对象的指针
	ptr->display(); //"对象指针->成员名"
}
int main() { 
	Base base; //声明Base类对象
	Derived derived; //声明Derived类对象
	fun(&base); //用Base对象的指针调用函数
	fun(&derived); //用Derived对象的指针调用函数
	return 0;
}

结果:

Base::display()
Base::display()

继承时的构造函数

  • 基类的构造函数不被继承,派生类中需要声明自己的构造函数
  • 定义构造函数时,只需要对本类中新增成员进行初始化,对继承来的基类成员的初始化,自动调用基类构造函数完成。
  • 派生类的构造函数需要给基类的构造函数传递参数
单一继承时的构造函数
派生类名::派生类名(基类所需的形参,本类成员所需的形参)
	:基类名(参数表), 本类成员初始化列表
{
	//其他初始化;
};

单一继承时的构造函数举例:

#include <iostream>
using namespace std;
class B {
	public:
		B(int i) {
			b=i; cout<<"B's constructor called."<<endl;
		}
		~B(){ cout<<"B's destructor called."<<endl; }
		void print() const{ cout<<b<<endl; }
	private:
		int b;
};
class C: public B {
	public:
		C(int i,int j): B(i), c(j){
			cout<<"C's constructor called." << endl;
		}
		~C(){ cout<<"C's destructor called." << endl; }
		void print() const { B::print(); cout<<c<<endl;}
	private:
		int c;
};
int main() {
	C obj(5, 6);
	obj.print();
	return 0;
}

结果:

B's constructor called.
C's constructor called.
5
6
C's destructor called.
B's destructor called.

多继承时的构造函数:

派生类名::派生类名(参数表):基类名1(基类1初始化参数表), 基类名2(基类2初始化参数表), ...
,本类成员初始化列表
{
	//其他初始化;
};

派生类与基类的构造函数

  • 当基类中声明有缺省构造函数或未声明构造函数时,派生类构造函数可以不向基类构造函数传递参数,也可以不声明,构造派生类的对象时,基类的缺省构造函数将被调用
  • 当需要执行基类中带形参的构造函数来初始化基类数据时,派生类构造函数应在初始化列表中为基类构造函数提供参数

多继承且有内嵌对象时的构造函数

派生类名::派生类名(形参表):基类名1(参数),基类名2(参数), ...
,本类对象成员和基本类型
成员初始化列表
{
	//其他初始化;
};

构造函数的执行顺序

  1. 调用基类构造函数,调用顺序按照它们被继承时声明的顺序(从左向右)
  2. 本类成员初始化列表中的基本类型成员和对象成员进行初始化,初始化顺序按照它们在类中声明的顺序。对象成员初始化是自动调用对象所属类的构造函数完成的
  3. 执行派生类的构造函数体中的内容

派生类构造函数举例:

例一:

#include <iostream>
using namespace std;
class Base1 { //基类Base1构造函数有参数
	public:
		Base1(int i) { cout<<"Constructing Base1 "<<i<<endl;}
};
class Base2 { //基类Base2构造函数无参数
	public:
		Base2( ) { cout<<"Constructing Base2 *"<<endl;}
};
//派生新类Derived,注意基类名的顺序
class Derived:public Base2,public Base1{
	public:
		Derived(int a,int b):Base1(a){ c=b;}
	private:
		int c;
};
int main() {
	Derived obj(1, 2);
	return 0;
}
/*
运行结果:
Constructing Base2 *
Constructing Base1 1
*/

例二:

#include <iostream>
using namespace std;
class Base1 { //基类Base1构造函数有参数
	public:
		Base1(int i) { cout<<"Constructing Base1 "<<i<<endl;}
};
class Base2 { //基类Base2构造函数无参数
	public:
		Base2( ) { cout<<"Constructing Base2 *"<<endl;}
};
//派生新类Derived,注意基类名的顺序
class Derived{
	public:
		Derived(int a,int b):member1(a){ c=b;}
	private:
		//注意成员对象名的个数与顺序
		Base1 member1;
		Base2 member2;
		int c;
};
int main() {
	Derived obj(1, 2);
	return 0;
}
/*
运行结果:
Constructing Base1 1
Constructing Base2 *
*/

结合:

#include <iostream>
using namespace std;
class Base1 { //基类Base1构造函数有参数
	public:
		Base1(int i) { cout<<"Constructing Base1 "<<i<<endl;}
};
class Base2 { //基类Base2构造函数无参数
	public:
		Base2( ) { cout<<"Constructing Base2 *"<<endl;}
};
//注意基类名的个数与顺序,
//注意成员对象名的个数与顺序
class Derived: public Base2, public Base1{
	public:
		Derived(int a,int b):Base1(a),member1(b){ }
	private:
		Base1 member1;
		Base2 member2;
};

int main() {
	Derived obj(1, 2);
	return 0;
}
/*
运行结果:
Constructing Base2 *
Constructing Base1 1
Constructing Base1 2
Constructing Base2 *
*/

复制构造函数

  • 若建立派生类对象时没有编写复制构造函数,编译器会生成一个隐含的复制构造函数,该函数先调用基类的复制构造函数,再为派生类新增的成员对象执行拷贝
  • 若编写派生类的复制构造函数,则需要为基类相应的复制构造函数传递参数,例如:C::C(const C &c1): B(c1) {…}

析构函数

  • 析构函数也不被继承,派生类自行声明
  • 声明方法与一般(无继承关系时)类的析构函数相同
  • 不需要显式地调用基类的析构函数,系统会自动隐式调用
  • 析构函数的调用次序与构造函数相反

派生类析构函数举例:

#include <iostream>
using namespace std;
class Base1 { //基类Base1,构造函数有参数
	public:
		Base1(int i) { cout << "Constructing Base1 " << i << endl; }
		~Base1() { cout << "Destructing Base1" << endl; }
};
class Base2 { //基类Base2,构造函数有参数
	public:
		Base2(int j) { cout << "Constructing Base2 " << j << endl; }
		~Base2() { cout << "Destructing Base2" << endl; }
};
class Base3 { //基类Base3,构造函数无参数
	public:
		Base3() { cout << "Constructing Base3 *" << endl; }
		~Base3() { cout << "Destructing Base3" << endl; }
};
class Derived: public Base2, public Base1, public Base3 {
	//派生新类Derived,注意基类名的顺序
	public: //派生类的公有成员
		Derived(int a, int b, int c, int d): Base1(a), member2(d),
		member1(c), Base2(b) { }
	//注意基类名的个数与顺序,注意成员对象名的个数与顺序
	private: //派生类的私有成员对象
		Base1 member1;
		Base2 member2;
		Base3 member3;
};
int main() {
	Derived obj(1, 2, 3, 4);
	return 0;
}

分析:

建立派生类对象时, 3种构造函数的调用顺序依次为:基类的构造函数->成员对象的构造函数->派生类的构造函数

结果:

Constructing Base2 2
Constructing Base1 1
Constructing Base3 *
Constructing Base1 3
Constructing Base2 4
Constructing Base3 *
Destructing Base3
Destructing Base2
Destructing Base1
Destructing Base3
Destructing Base1
Destructing Base2

例二:

#include <iostream>
#include <string>
using namespace std;
class CBug {
    int legNum, color;
	public:
	    CBug(int ln, int c1) : legNum(ln), color(c1){
	        cout << "CBug Constructor" << endl;
	    };
	    ~CBug(){
	        cout << "CBug Destructor" << endl;
	    }
	    void Printlnfo(){
	        cout << legNum << "," << color << endl;
	    }

};
class CFlyingBug : public CBug{
    int wingNum;
	public:
	    //CFlyingBug(){}  若不注释掉则会编译出错
	    CFlyingBug(int ln, int c1, int wn) : CBug(ln, c1), wingNum(wn){
	        cout << "CFlyingBug Constructor" << endl;
	    }
	    ~CFlyingBug(){
	        cout << "CFlyingBug Destructor" << endl;
	    }
};
int main() {
    CFlyingBug fb(2, 3, 4);
    fb.Printlnfo();
    return 0;
}

分析:

  • 第 20 行如果没有注释掉会编译出错。因为这个构造函数没有说明在派生类对象用该构造函数初始化的情况下,其基类对象该如何初始化——这也就意味着基类对象应该用无参构造函数初始化,可是 CBug 类并没有无参构造函数,所以编译会出错
  • 第 26 行中的“CBUg(ln, c1)”指明了在派生类对象用该构造函数初始化的情况下,其基类对象的初始化方式
  • 派生类对象生成时要先执行基类构造函数,消亡时要先执行自身析构函数,再执行基类析构函数

结果:

CBug Constructor
CFlyingBug Constructor
2,3
CFlyingBug Destructor
CBug Destructor

作用域限定

当派生类与基类中有相同成员时:

  • 若未特别限定,则通过派生类对象使用的是派生类中的同名成员。
  • 如要通过派生类对象访问基类中被隐藏的同名成员,应使用基类名和作用域操作符(::)来限定

例子:

#include <iostream>
using namespace std;
class Base1 { //定义基类Base1
	public:
		int var;
		void fun() { cout << "Member of Base1" << endl; }
};
class Base2 { //定义基类Base2
	public:
		int var;
		void fun() { cout << "Member of Base2" << endl; }
};
class Derived: public Base1, public Base2 { //定义派生类Derived
	public:
		int var; //同名数据成员
		void fun() { cout << "Member of Derived" << endl; } //同名函数成员
};
int main() {
	Derived d;
	Derived *p = &d;
	d.var = 1; //对象名.成员名标识
	d.fun(); //访问Derived类成员
	d.Base1::var = 2; //作用域操作符标识
	d.Base1::fun(); //访问Base1基类成员
	p->Base2::var = 3; //作用域操作符标识
	p->Base2::fun(); //访问Base2基类成员
	return 0;
}

输出:

Member of Derived
Member of Base1
Member of Base2

二义性问题

  • 当派生类从多个基类派生,而这些基类又从同一个基类派生,则在访问此共同基类中的成员时,将产生二义性——采用虚基类来解决
  • 在多继承时,基类与派生类之间,或基类之间出现同名成员时,将出现访问时的二义性(不确定性)——采用虚函数或同名隐藏来解决

多继承同名隐藏举例:

#include <iostream>
using namespace std;
class Base0 { //定义基类Base0
	public:
		int var0;
		void fun0() { cout << "Member of Base0 , " << var0 <<endl; }
};
class Base1: public Base0 { //定义派生类Base1
	public: //新增外部接口
		int var1;
};
class Base2: public Base0 { //定义派生类Base2
	public: //新增外部接口
		int var2;
};
class Derived: public Base1, public Base2 {//定义派生类Derived
	public: //新增外部接口
		int var;
		void fun() { cout << "Member of Derived" << endl; }
};
int main() { //程序主函数
	Derived d; //定义Derived类对象d
	d.Base1::var0 = 2; //使用直接基类
	d.Base1::fun0();
	d.Base2::var0 = 3; //使用直接基类
	d.Base2::fun0();
	return 0;
}

分析:
在这里插入图片描述

输出:

Member of Base0 , 2
Member of Base0 , 3

虚基类

  • 虚基类的引入:用于有共同基类的场合
  • 声明 以virtual修饰说明基类,例:class B1:virtual public B
  • 注意:在第一级继承时就要将共同基类设计为虚基类
  • 作用:
  1. 主要用来解决多继承时可能发生的对同一基类继承多次而产生的二义性问题
  2. 为最远的派生类提供唯一的基类成员,而不重复产生多次拷贝

代码:

#include <iostream>
using namespace std;
class Base0 { //定义基类Base0
	public:
		int var0;
		void fun0() { cout << "Member of Base0 , " << var0 <<endl; }
};
class Base1: virtual public Base0 { //定义派生类Base1
	public: //新增外部接口
		int var1;
};
class Base2: virtual public Base0 { //定义派生类Base2
	public: //新增外部接口
		int var2;
};
class Derived: public Base1, public Base2 {//定义派生类Derived
	public: //新增外部接口
		int var;
		void fun() { cout << "Member of Derived" << endl; }
};
int main() { //程序主函数
	Derived d; //定义Derived类对象d
	d.var0 = 2; //直接访问虚基类的数据成员
	d.fun0(); //直接访问虚基类的函数成员
	return 0;
}

分析:
在这里插入图片描述

结果:

Member of Base0 , 2

虚基类及其派生类构造函数

  • 建立对象时所指定的类称为最(远)派生类
  • 虚基类的成员是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的
  • 在整个继承结构中,直接或间接继承虚基类的所有派生类,都必须在构造函数的成员初始化表中给出对虚基类的构造函数的调用。如果未列出,则表示调用该虚基类的默认构造函数
  • 在建立对象时,只有最派生类的构造函数调用虚基类的构造函数,该派生类的其他基类对虚基类构造函数的调用被忽略

有虚基类时的构造函数举例:

#include <iostream>
using namespace std;
class Base0 { //定义基类Base0
	public:
		Base0(int var) : var0(var){}
		int var0;
		void fun0() { cout << "Member of Base0 , " << var0 <<endl; }
};
class Base1: virtual public Base0 { //定义派生类Base1
	public: //新增外部接口
		Base1(int var) : Base0(var){}
		int var1;
};
class Base2: virtual public Base0 { //定义派生类Base2
	public: //新增外部接口
		Base2(int var) : Base0(var){}
		int var2;
};
class Derived: public Base1, public Base2 {//定义派生类Derived
	public: //新增外部接口
		Derived(int var) : Base0(var), Base1(var), Base2(var){}
		int var;
		void fun() { cout << "Member of Derived" << endl; }
};
int main() { //程序主函数
	Derived d(1); //定义Derived类对象d
	d.var0 = 2; //直接访问虚基类的数据成员
	d.fun0(); //直接访问虚基类的函数成员
	return 0;
}

结果:

Member of Base0 , 2

多态性

基类和派生类举例:

#include <iostream>
using namespace std;
class Base { //基类Base定义
	public:
		void display() const {
			cout << "Base::display()" << endl;
		}
};
//公有派生类Derived定义
class Derived: public Base {
	public:
		void display() const {
			cout << "Derived::display()"<<endl;
		}
};
void fun(Base *ptr) { //参数为指向基类对象的指针
	ptr->display(); //"对象指针->成员名"
}
int main() { 
	Base base; //声明Base类对象
	Derived derived; //声明Derived类对象
	fun(&base); //用Base对象的指针调用函数
	fun(&derived); //用Derived对象的指针调用函数
	return 0;
}

结果:

Base::display()
Base::display()

代码:

#include <iostream>
using namespace std;
class Base { //基类Base定义
	public:
		virtual void display() const {
			cout << "Base::display()" << endl;
		}
};
//公有派生类Derived定义
class Derived: public Base {
	public:
		void display() const {
			cout << "Derived::display()"<<endl;
		}
};
void fun(Base *ptr) { //参数为指向基类对象的指针
	ptr->display(); //"对象指针->成员名"
}
int main() {
	Base base; //声明Base类对象
	Derived derived; //声明Derived类对象
	fun(&base); //用Base对象的指针调用函数
	fun(&derived); //用Derived对象的指针调用函数
	return 0;
}

结果:

Base::display()
Derived::display()

虚函数

  • 用virtual关键字说明,虚函数是实现运行时多态性基础
  • C++中的虚函数是动态绑定的函数
  • 虚函数必须是非静态的成员函数,经过派生之后,就可以实现运行过程中的多态

虚函数成员

C++中引入了虚函数的机制在派生类中可以对基类中的成员函数进行覆盖(重定义)
虚函数的声明:
virtual 函数类型 函数名 (形参表)
{
	函数体
}

纯虚函数

纯虚函数是一个在基类中声明的虚函数,
它在该基类中没有定义具体的操作内容,
要求各派生类根据实际需要重写该函数,

纯虚函数的声明格式为:
virtual 函数类型 函数名(参数表) = 0;

带有纯虚函数的类称为抽象类:
class 类名
{
	virtual 类型 函数名(参数表)=0; //纯虚函数
	...
}

抽象类

  • 定义:抽象类是带有纯虚函数的类,对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类去实现
  • 作用:通过多态性调用虚函数
  • 抽象类只能作为基类来使用,不能声明抽象类的对象
  • 构造函数不能是虚函数,析构函数可以是虚函数

抽象基类的例子:

#include <iostream>
#define PI 3.1416
using namespace std;
class Shape{
	public:
		virtual double area() const=0;
		virtual void show() const=0;
};
//Circle类
class Circle :public Shape{
	public:
		Circle(double r1=1.0){r=r1;};
		double area()const{return PI*r*r;};
		void show()const{cout<<"I am a Circle:";}
	private:
		double r;
};
//Rectangle类定义
class Rectangle :public Shape{
	public:
		Rectangle(double=1.0,double=1.0);
		double area() const;
		void show() const;
	private:
		double length;
		double width;
};
//Rectangle类实现
Rectangle::Rectangle(double a, double b){
	length = a;
	width = b;
}
double Rectangle::area() const{
	return length*width;
}
void Rectangle::show() const{
	cout<<"I am a Rectangle: ";
}
void callArea(Shape &obj){
	obj.show();
	cout<<"area ="<<obj.area()<<endl;
}
int main(){
	Circle cir(2.5);
	Rectangle rec(2.4,5.3);
	callArea(cir);
	callArea(rec);
	return 0;
}

结果:

I am a Circle:area =19.635
I am a Rectangle: area =12.72

多态的类型

  • 函数重载多态
  • 运算符重载多态
  • 虚函数和抽象类

根据多态性作用的时机可以分为

  • 编译时的多态
  • 运行时的多态

多态的实现

  • 绑定是指将一个标识符名和一个存储地址联系在一起的过程
  • 编译时的多态,绑定工作在编译连接阶段完成的情况称为静态绑定
  • 运行时的多态,绑定工作在程序运行阶段完成的情况称为动态绑定

运算符重载

两种形式

  • 重载为类的成员函数
  • 重载为非成员函数
声明形式:
函数类型 operator 运算符 (形参)
{
	......
}
重载为类成员函数时参数个数=原操作数个数-1
重载为非成员函数时参数个数=原操作数个数
且至少应该有一个自定义类型的形参

示例:

#include<bits/stdc++.h>
#define LL long long
#define fo(i,a,b) for(int i=a;i<b;i++)
using namespace std;
class Complex
{
	public:
		Complex(double r=0, double i=0):real(r), imag(i){	}
		Complex operator+(Complex c) const;//重载双目运算符'+'
		Complex operator-=(Complex c); //重载双目运算符'-='
		friend Complex operator-(Complex c1,Complex c2);//重载双目运算符'-'//不能加const,因为只有类成员函数才能加const
		void Display()const;
	private:
		double real;
		double imag;
};
Complex Complex::operator+(Complex c)const{
	return Complex(real+c.real,imag+c.imag);
}
Complex Complex::operator-=(Complex c){
	real=real-c.real;imag=imag-c.imag;
	return *this;
}//比较特殊
Complex operator-(Complex c1,Complex c2){//不能加const,因为只有类成员函数才能加const
	return Complex(c1.real-c2.real,c1.imag-c2.imag);
}

void Complex::Display()const
{
	cout << "(" << real << ", " << imag << ")" << endl;
}

int main()
{
	double r, m;
	cin >> r >> m;
	Complex c1(r, m);
	cin >> r >> m;
	Complex c2(r, m);
	Complex c3 = c1+c2;
	c3.Display();
	c3 = c1-c2;
	c3.Display();
	c3 -= c1;
	c3.Display();
	return 0;
}

题一:

请定义一个分数类,拥有两个整数的私有数据成员,分别表示分子和分母(分母永远为正数,符号通过分子表示)
重载运算符加号"+",实现两个分数的相加,所得结果必须是最简分数

输入:
第一行的两个数 分别表示 第一个分数的分子和分母(分母不为0)
第二行的两个数 分别表示 第二个分数的分子和分母

输出:
第一个数表示分子,第二个数表示分母(若分数代表的是整数,则不输出分母)

输入样例:
1  5
2  5

输出样例:
3 5

代码:

#include<bits/stdc++.h>
#define LL long long
#define fo(i,a,b) for(int i=a;i<b;i++)
using namespace std;
//欧几里得算法
int gcd(int a,int b){return b?gcd(b,a%b):a;}
class p{
	private:
		int up,down;
	public:
		p(int t1,int t2){cin>>t1>>t2;up=t1;down=t2;}//构造函数1 
		p(){}//默认构造函数2
		p operator+(p f){
			p t;
			t.down=down*f.down;
			t.up=down*f.up+up*f.down;
			int g=gcd(t.down,t.up);
			t.down/=g; t.up/=g;//分数化简(例如3/6->1/2)
			return t;
		}//重载+
		friend ostream &operator<<(ostream &out, p &f){
			if(f.up%f.down!=0)out<<f.up<<" "<<f.down;//整数要分类讨论
			else out<<f.up/f.down;
			return out;
		}//重载cout
};
int main(){
	p p1(0,1),p2(0,1);//调用构造函数1 
	p p3=p1+p2;//调用默认构造函数2,并重载+ 
	cout<<p3;//重载cout 
	return 0;
}

题二:

题目描述:
建立一个复数类,实数和虚数是其私有数据成员
建立一个>(大于号)的运算符重载,比较两个复数间模的大小

输入格式:
测试输入包含若干测试用例,每个测试用例占一行。每个测试用例包括四个数字
前两个数字分别表示第一个复数的实部和虚部,第三个和第四个数字分别表示第二个复数的实部和虚部
每个数字之间用空格间隔。当读入一个测试用例是0 0 0 0时输入结束,相应的结果不要输出

输出格式:
对每个测试用例输出一行,
当第一个复数的模大于第二个复数的模时,输出 true
当第一个复数的模小于或等于第二个复数的模时,输出false

输入样例:
3 5 4 0
0 3 4 1
0 0 0 0

输出样例:
true
false

代码:

#include<bits/stdc++.h>
#define LL long long
#define fo(i,a,b) for(int i=a;i<b;i++)
using namespace std;
class p{
	int a,b;
	public:
		p(int t1=0,int t2=1):a(t1),b(t2){}
		int operator>(p f){
			if(a*a+b*b>f.a*f.a+f.b*f.b)return 1;
			else return 0;
		}
};
int a,b,c,d;
int main(){
	while(1){
		cin>>a>>b>>c>>d;
		if(a==0&&b==0&&c==0&&d==0)break;
		p p1(a,b),p2(c,d);
		if(p1>p2)cout<<"true";
		else cout<<"false";
		cout<<"\n";
	}
	return 0;
}

函数模板和类模板

题一:

题目描述:
复数类Complex有两个数据成员:a和b, 分别代表复数的实部和虚部
并有若干构造函数和一个重载-(减号,用于计算两个复数的距离)的成员函数
要求设计一个函数模板
template < class T >
double dist(T a, T b)intfloat,Complex或者其他类型的数据,返回两个数据的间距
以上类名和函数模板的形式,均须按照题目要求,不得修改

输入格式:
每一行为一个操作,每行的第一个数字为元素类型,1为整型元素,2为浮点型元素,3为Complex类型
若为整型元素,接着输入两个整型数据
若为浮点型元素,接着输入两个浮点型数据
若为Complex型元素,输入两个Complex型数据(a1 b1 a2 b2)
输入0时标志输入结束

输出格式:
对每个输入,每行输出一个间距值

输入样例:
1 2 5
3 2 4 5 9
2 2.2 9.9
0

输出样例:
3
5.83095
7.7

代码:

#include<bits/stdc++.h>
using namespace std;
class Complex{
	private:
		double a,b;
	public:
		Complex(double a1,double b1):a(a1),b(b1){}
		double operator-(Complex f){
			return sqrt(fabs((a-f.a)*(a-f.a)+(b-f.b)*(b-f.b)));
		}				
};
template < class T >
double dist(T a, T b){
	return fabs(a-b);
}

int main(){
	int k;
	cin>>k;
	while(k){
		if(k==1){
			int a,b;
			cin>>a>>b;
			cout<<dist(a,b)<<endl;
		}
		else if(k==2){
			double a,b;
			cin>>a>>b;
			cout<<dist(a,b)<<endl;
		}
		else if(k==3){
			double a,b,c,d;
			cin>>a>>b>>c>>d;
			Complex t1(a,b),t2(c,d);
			cout<<dist(t1,t2)<<endl;
		}
		cin>>k;
	}
	return 0;
}

题二:

题目描述:
两个类如下设计:类Time有三个数据成员,hh,mm,ss,分别代表时,分和秒
并有若干构造函数和一个重载-(减号)的成员函数
类Date有三个数据成员,year,month,day分别代表年月日,并有若干构造函数和一个重载><)(大于号或者小于号)的成员函数。
要求设计一个函数模板
template <class T>
double maxn(T x[], int len);intfloat,time和date或者其他类型的数据,返回最大值。
main主函数有如下变量的定义:
int intArray[100];
double douArray[100];
Time timeArray[100];
Date dateArray[100];
其中,hh = 3600 ss, mm = 60 ss, year = 365 day, month = 30 day,对于Time和Date类型,数据在转换成ss或者day后进行运算

输入格式:
每行为一个操作,每行的第一个数字为元素类型,1为整型元素,2为浮点型元素,3为Time类型,4为Date类型
若为整型元素,接着输入整型数据,以0结束
若为浮点型元素,接着输入浮点型数据,以0结束
若为Time型元素, 输入Time型数据(hh1 mm1 ss1 hh2 mm2 ss2),以0结束
若为Date型数据,输入Date型数据(year1 month1 day1 year2 month2 day2),以0结束
输入-1时表示全体输入结束。

输出格式:
对每次输入,输出一个最大值。

样例输入:
1 4 5 9 3 7 0
2 4.4 5.5 6.9 3.2 2.7 0
3 18 21 22 18 20 31 18 21 49 0
4 2013 5 14 2013 5 15 2013 4 1 0
-1

样例输出:
9
6.9
18 21 49
2013 5 15

代码:

#include<bits/stdc++.h>
#define LL long long
#define fo(i,a,b) for(int i=a;i<b;i++)
using namespace std;
class Time{
	private:
		int hh,mm,ss;
	public:
		Time(int h,int m,int s):hh(h),mm(m),ss(s){}
		Time(){};		
		bool operator-(Time f){
			if(hh*3600+mm*60+ss-f.hh*3600-f.mm*60-f.ss>0)return 1;
			else return 0;
		}
		void pri(){
			cout<<hh<<" "<<mm<<" "<<ss<<" "<<endl;
		}
};
class Date{
	private:
		int year,month,day;
	public:
		Date(int y,int m,int d):year(y),month(m),day(d){}
		Date(){};
		bool operator>(Date f){
			if(year*365+month*30+day-f.year*365-f.month*30-f.day>0)return 1;
			else return 0;
		}
		bool operator-(Date f){
			if(year*365+month*30+day-f.year*365-f.month*30-f.day>0)return 1;
			else return 0;
		}
		void pri(){
			cout<<year<<" "<<month<<" "<<day<<" "<<endl;
		}
		
};
template <class T>
T maxn(T x[], int len){
	T m=x[0];
	fo(i,0,len-1){
		if((m-x[i+1])<=0)m=x[i+1];
	}
	return m;
}

int main(){
	int intArray[100];
	double douArray[100];
	Time timeArray[100];
	Date dateArray[100];
	int k;
	cin>>k;
	while(k>0){
		if(k==1){
			int i=0,t,m;
			cin>>t;
			while(t){
				intArray[i++]=t;
				cin>>t;
			}
			m=maxn(intArray,i);
			cout<<m<<endl;
		}
		else if(k==2){
			int i=0;
			double t,m;
			cin>>t;
			while(t){
				douArray[i++]=t;
				cin>>t;
			}
			m=maxn(douArray,i);
			cout<<m<<endl;
		}
		else if(k==3){
			int i=0,t1,t2,t3;
			Time m;
			cin>>t1;
			while(t1){
				cin>>t2>>t3;
				Time t(t1,t2,t3);
				timeArray[i++]=t;
				cin>>t1;
			}
			m=maxn(timeArray,i);
			m.pri();
		}
		else if(k==4){
			int i=0,t1,t2,t3;
			Date m;
			cin>>t1;
			while(t1){
				cin>>t2>>t3;
				Date t(t1,t2,t3);
				dateArray[i++]=t;
				cin>>t1;
			}
			m=maxn(dateArray,i);
			m.pri();
		}
		cin>>k;
	}
	return 0;
}

流类库和输入输出

例子:

定义一个Dog类,包括体重和年龄两个数据成员及其成员函数
声明一个实例dog1,体重5,年龄10,使用I/O流把dog1的状态写入磁盘文件
再声明一个实例dog2,通过读取文件dog1的状态赋给dog2
分别用文本方式和二进制方式操作文件

文本方式操作文件:

#include<fstream>
#include<iostream>
using namespace std;
class dog
{	
public:
	int weight;
	int age;	
	dog(int weight1=0,int age1=0)
	{
		weight=weight1;
		age=age1;
	}//这样的构造函数0参,1参,2参都可以
	~dog(){}
};
int main()
{
	dog dog1(5,10);
	dog dog2;
	ofstream fout;
	fout.open("out.txt");
	if (!fout)
	{
		cout << "Unable to open for writing.\n";
		return(1);//returns 1 on error
	}
	fout<<dog1.weight<<"\n"<<dog1.age<<endl;
	ifstream fint;
	fint.open("out.txt");
	if (!fint)
	{
		cout << "Unable to open  for reading.\n";
		return(1);
	}
	fint>>dog2.weight>>dog2.age;
	cout<<dog2.weight<<endl<<dog2.age<<endl;
	fout.close();
}

二进制操作文件:

#include<fstream>
#include<iostream>
using namespace std;
class dog
{	
public:
	int weight;
	int age;	
	dog(int weight1=0,int age1=0)
	{
		weight=weight1;
		age=age1;
	}
	~dog(){}
};
int main()
{
	dog dog1(5,10);
	dog dog2;
	ofstream fdata("file.dat",ios::binary);
	if (!fdata)
	{
		cout << "Unable to open  for writing.\n";
		return(1);//returns 1 on error
	}
	fdata.write((char*)(&dog1),sizeof(dog1));
	fdata.close();
	ifstream fint1("file.dat",ios::binary);
	if (!fint1)
	{
		cout << "Unable to open  for reading.\n";
		return(1);
	}
	fint1.read((char*)(&dog2),sizeof(dog2));//read和write格式一样
	cout<<dog2.age<<endl<<dog2.weight<<endl;
	fint1.close();
	return 0;
}

注意:

  • 不要#include<fstream.h>,因为fstream.h是比较旧的标准,编译不通过,必须改用标准的c++写法才行(在标准C++中,已经使用<fstream>取代<fstream.h>,所有的C++标准头文件都是无后缀的)
  • 二进制文件会复杂一点,首先要注意不再使用插入和提取操作符(<<>> 操作符),可以这么做,但它不会用二进制方式读写。必须使用read()write()方法读取和写入二进制文件
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值