面向 过程程序设计主要特征: 程序 = 过程 + 调用
面向 对象程序设计主要特征: 程序 = 对象 + 消息
面向对象程序设计的基本特征:
- 抽象
- 封装
- 继承
- 多态
运算符new和delete
C语言使用函数malloc和free动态分配内存和释放动态分配的内存,C++使用运算符new和delete能更简单地进行内存的分配和释放。优点如下:
(1)使用malloc函数时必须使用sizeof函数计算所需字节数,new可以根据数据类型自动计算所需内存大小,减少发生错误可能性
(2)new能够自动返回正确的指针类型,malloc函数必须在程序中进行强制类型转换才能返回正确的指针类型。
运算符new用于内存分配的最基本形式:指针变量名=new类型
运算符delete用于释放运算符new分配的存储空间,最基本形式:delete 指针变量名
#include<iostream>
using namespace std;
int main()
{
int *ptr; //定义一个整型指针变量ptr
ptr=new int; //动态分配一个整型存储空间,并将首地址赋给指针变量ptr
*ptr=10;
cout<<*ptr;
delete ptr; //释放指针变量ptr指向的存储空间
return 0;
}
在程序运行过程中,运算符new从堆的一块自由存储区为程序分配一块与类型字节数相适应的内存空间,并将该块内存的首地址存于指针变量中,存储空间释放之后,指针变量保存new分配的内存的首地址。
对new和delete使用的几点说明:
int *pi=new int[10];
为数组分配内存空间需在类型名后面缀上数组大小
int *p; p=new int(99);
分配内存的同时进行初始化,将99作为初始值
delete [ ]指针变量名;
释放动态分配的数组存储区
用new分配的存储空间不会自动释放,只能通过delete释放。如果没有分配足够的内存,则动态分配空间失败,有些编译系统返回空指针NULL。
//对动态分配是否成功进行检查
#include<iostream>
using namespace std;
int main()
{ int *p; p=new int;
if(!p)
{ cout<<“分配失败\n”;
return 1; }
*p=20; cout<<*p;
delete p;
return 0;
}
类与对象的基本概念
在默认情况下,类的成员是私有的,结构体的成员是公有的。声明为私有的成员对外界是隐蔽的,在类外不能直接访问。在实际应用中,一般把需要保护的数据设置为私有(隐蔽起来),把成员函数设置为公有的(作为类与外界的接口)。
成员函数的定义
- 在类外定义成员函数——程序设计的良好习惯
class Point{
public:
void setpoint(int,int);
int getx();
int gety();
private:
int x,y;
};
void Point::setpoint(int a,int b)
{ x=a;
y=b; }
int Point::getx()
{ return x; }
int Point::gety()
{ return y; }
减少类体的长度,使声明简洁明了,便于阅读,有助于把类的接口和类的实现细节相分离,隐藏了执行的细节。
- 隐式定义内联函数(内置函数),将成员函数定义在类的内部
class Point{
public:
void setpoint(int a,int b)
{ x=a;
y=b; }
int getx();
{ return x; }
int gety();
{ return y; }
private:
int x,y;
};
将成员函数作为内联函数进行处理,可减少调用函数的开销,提高执行效率,但增加了编译后的代码长度,只有相当简短的成员函数才定义为内联函数。
- 显式定义内联成员函数
class Point{
public:
inline void setpoint(int,int);
inline int getx();
inline int gety();
private:
int x,y;
};
inline void Point::setpoint(int a,int b)
{ x=a;
y=b; }
inline int Point::getx()
{ return x; }
inline int Point::gety()
{ return y; }
消除函数调用时的系统开销,用空间换时间,因此,通常只有规模很小而使用频繁的函数才定义为 内联函数,一般内联函数中不能含有复杂的控制语句,如:for语句和switch语句等。
对象的定义
- 声明类的同时,直接定义对象
class Point{
……
}op1,op2;
- 声明类之后,在使用时定义对象
class Point{
……
};
Point op1,op2;
声明一个类便声明了一种类型,它不接收和存储具体的值,只作为生成具体对象的一种样板,只有定义了对象之后,系统才能为对象分配存储空间,以存放对象中的成员。因此,在类的声明中不能给数据成员赋初值。
对象中成员的访问
- 通过对象名和对象选择符访问对象中的成员
一般形式:
对象名.数据成员名
对象名.成员函数名
其中“.”叫做对象选择符,简称点运算符 - 通过指向对象的指针访问对象中的成员
使用“->”操作符,如:
class Data{
public:
int year;
};
Date d, *ptr; //定义对象d和指向类Data的指针变量ptr
ptr = &d; //使ptr指向对象d
cout<<ptr->year; //输出ptr指向对象中的成员year
d.year
(*ptr).year
ptr->year
三者等价
- 通过对象的引用访问对象中的成员
如果为一个对象定义了一个引用,也就是为这个对象起了一个别名。
class Data{
public:
int year;
};
Date d1; //定义类Data的对象d1
Date &d2=d1; //定义类Data的引用d2,并用对象d1进行初始化
cout<<d1.year; //输出对象d1中的数据成员year
cout<<d2.year; //输出对象d2中的数据成员year
d2是d1的引用,d2和d1占用相同的存储单元,因此两者是相同的。
构造函数与析构函数
构造函数是一种特殊的成员函数,它可以由用户提供,也可以由系统自动生成,当声明一个类的对象时,编译程序需为对象分配存储空间,由构造函数来完成存储空间的分配,进行初始化。它不需要用户调用,而是在建立对象时自动执行。
构造函数的名字必须与类名相同,不能由用户任意命名,它可以有任意类型的参数,但不能具有返回值类型。
析构函数也属于某一个类,可以由用户提供,也可以由系统自动生成,当撤销类的对象时,析构函数就回收存储空间,做一些善后工作。
构造函数的建立
- 类内建立构造函数
class Complex{
private:
double real;
double imag;
public:
Complex(double r,double i) //定义构造函数,其名与类名相同,没有返回值类型
{ real=r;imag=i;} //在构造函数中,对私有数据成员赋值
double abscomplex()
{ double t;
t=real*real+imag*imag;
return sgrt(t); }
};
- 类外建立构造函数
class Complex{
private:
double real;
double imag;
public:
Complex(double r,double i); //声明构造函数原型
double abscomplex(); //声明成员函数原型
};
Complex::Complex(double r,double i) //在类外定义构造函数
{ real=r;imag=i;} //用赋值语句对数据成员赋初值
double Complex::abscomplex() //在类外定义成员函数
{ double t;
t=real*real+imag*imag;
return sgrt(t); }
构造函数的调用
在建立对象的同时,采用构造函数给数据成员赋初值,通常有两种形式:
形式1:类名 对象名[(实参表)]
例:Complex A(1,2)
——定义类Complex的对象A时调用构造函数,并给数据成员赋初值。
形式2:类名 * 指针变量名=new 类名[(实参表)]
例:Complex *p=new Complex(1,2); delete p;
——开辟一段空间存放一个类的对象,该对象的地址存放在指针变量p中,当new建立的对象不再需要时,用delete释放内存。
成员初始化列表
在C++中某些类型的成员是不允许在构造函数中用赋值语句直接赋值,如,用const修饰的数据成员,引用类型的数据成员,是不允许用赋值语句直接赋值的。因此,只能用成员初始化列表进行初始化。
形式如下:类名::构造函数名([参数表]):[成员初始化列表]
成员初始化列表为:数据成员1(初始值1),数据成员2(初始值2),……
#include<iostream>
using namespace std;
class A{
public:
A(int x1): x(x1),rx(x),pi(3.14)
{ } //用成员初始化列表对引用类型和const修饰的数据成员初始化
void print(){ cout<<x<<rx<<pi<<endl; }
private:
int x; int & rx; const double pi;
};
int main()
{ A a(10);
a.print();
return 0; }
数据成员按照它在类中的声明顺序进行初始化,与成员初始化列表顺序无关。
D:: D(int i):mem2(i),mem1(mem2+1){ }; D d(15);
运行得mem2为15,mem1为随机数,mem1声明在前,因而mem1在mem2之前被初始化。
析构函数
析构函数是一种特殊的成员函数,执行与构造函数相反的操作,通常用于释放内存空间。
Complex::~Complex() //显示定义析构函数
{ cout<<"Destructor called."<<endl; }
Complex::~Complex() //系统自动生成的默认析构函数
{ }
它有一下一些特点:
1、析构函数名与类名相同,前面必须加上波浪号~。
2、析构函数不返回任何值,不能说明函数类型,void也不行。
3、析构函数没有参数,不能被重载,一个类可以有多个构造函数,但只能有一个析构函数。
4、撤销对象时,系统自动调用析构函数。