过程性编程和面向对象编程
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 对象名;
- 常对象必须进行初始化,而且不能被更新。
- 由于常对象的值( 包括所有的数据成员的值)不能被改变,因此,通过常对象只能调用常成员函数,而不能调用类中的其他普通成员函数。