面向对象概念
类与对象
构造函数和析构函数
内存的动态分配★★★
对象数组和对象指针
函数参数的传递机制
友元
面向对象的三大特征:封装、继承、多态
先学习封装,看完类和对象之后再看继承和多态:
class Student
{
private:
lont no;
char name[10];
float score;
public:
Student();
void findNumber(long no);
void findName(char name[]);
void print();
}
//C:结构体
typedef struct Student
{
long no;
char name[10];
float score;
};
void findnumber(long no);
void findname(char name[]);
void Print();
class 类名
{
private:
数据成员或成员函数
protected:
数据成员或成员函数
public:
数据成员或成员函数
};
3.成员函数的定义
一般代码量较少的成员函数可以在类中定义,而代码量较大的成员函数通常在类中进行函数原型的声明,在类外对函数进行定义
返回类型 类名::函数名(参数表)
{
//函数体
}
如果在类外定义成员函数,则应该在所定义的成员函数名前加上类名,在类名和函数名之间应加上作用域运算符**“::”**,它说明成员函数从属哪个类。
class Book
{
private:
char title[20],author[10],publish[30];
float price;
public:
void Input();
void Print();
}b1,b2; //表示定义b1,b2是Book类的对象。
2)先声明类,在使用的时候再定义对象:类名 对象名列表 ,一般则个方法比较多,除非要全局变量
对象的使用:使用对象就是向对象发送消息,请求执行它的某个方法,熊二向外界提供所要求的服务,形式如下:对象名.成员函数名(实参表)
3)在使用对象赋值语句进行赋值语句时,两个对象的类型必须相同,如果类中存在指针,则不能简单的将一个对象的值赋给另一个对象,否则会产生错误
构造函数的功能是在创建对象时,给数据成员赋值,即对象的初始化。
析构函数的功能是释放一个对象,在对象删除之前,用它来做一些内存释放等清理工作
类名::类名(形参列表)
{
//函数语句;
}
构造函数的特点:
1)构造函数名字必须与类名相同
2)构造函数可以有任意类型的参数,但是没有返回值类型,也不能指定为void类型
3)定义对象的时候,系统会自动调用构造函数,就是给对象一个初始化
4)如果没有定义构造函数,系统会自动生成一个默认的构造函数,只负责对象的创建,不做初始化工作
5)构造函数可以重载
用户存储空间分为三个部分:程序区(代码区)、静态存储区(栈区)、动态存储空间(堆区)。
代码区存放程序代码,程序运行前就分配存储空间。
栈区存放局部变量、函数参数、函数返回值和临时变量
堆区是程序空间中存放着的一些空闲存储单元,这些空闲存储单元组成堆 ,在堆中创建的数据对象为堆对象,当创建对象时,堆中的一些存储单元从未分配状态变为已分配状态;当删除所创建的堆对象时,这些存储单元从已分配状态又编程未分配状态。
在C++中使用new
和delete
来实现在堆内存区中进行数据的动态分配和释放,在程序运行过程中申请和释放的存储单元称为堆对象
指针变量 = new T;
指针变量 = new T(初始值列表);
指针变量 = new T[元素个数];
T是数据类型名,表示在堆中建立一个T类型的数据
int *p;
float *p1;
p = new int(100); //让p指向一个类型为整型的堆地址,该地址中存放数值100
p1 = new float; //让p1指向一个类型为实型的堆地址
★★★ p = new int(100); //让p指向一个类型为整型的堆地址,该地址中存放数值100
★★★ p1 = new float; //让p1指向一个类型为浮点型的堆地址
2)用new创建对对象的格式:
类名* 指针名 = new 类名(构造函数参数列表)
Complex *c1 = new Complex(1.1,2.2);
new返回一个指定的合法数据类型的内存空间的首地址,若分配不成功,则返回一个空指针
3) new 可以为数组动态分配空间,这时应该在类型名后面指明数组大小。其中,元素个数是一个整型数值,可以是常数也可以是变量
int n ,*p;
cin >> n;
p = new int[n];
4)new不能对动态分配的数组存储区进行初始化~~
5)用new分配的空间,使用结束后只能用delete显式的释放,否则这部分空间将不能回收,从而造成内存泄漏。
delete 指针变量名
释放指针所指向的动态内存空间delete [] 指针变量名
释放为数组动态分配的内存#include
using namespace std;
class Point
{
private:
int x,y;
public:
Point(int a, int b);
~Point();
};
Point::Point(int a, int b)
{
cout << "constructor..." <<endl;
x = a;
y = b;
}
Point::~Point()
{
cout << "destructor..." <<endl;
}
int main()
{
Point *p = new Point(1, 3);
delete p;
return 0;
}
一,对象数组
数组元素可以是基本数据类型的数据,也可以是用户自定义数据类型的数据。
对象数组是指每一个数组元素都是对象的数组
对象数组的元素是对象,它不仅具有数据成员,而且还有函数成员。
类名 数组名[下标表达式];
数组名[下标].成员函数;
#include
using namespace std;
class Circle
{
private:
double radius;
public:
Circle(double r);
double Area();
~Circle();
};
Circle::Circle(double r)
{
cout << "construct..." <<endl;
radius = r;
}
double Circle::Area()
{
return 3.14*radius*radius;
}
Circle::~Circle()
{
cout << "destruct..." <<endl;
}
int main()
{
Circle c[3] = {1,3,5};
int i;
for(i = 0; i < 3; i++)
{
cout <<c[i].Area() <<endl;
}
return 0;
}
#include
using namespace std;
class Point
{
private:
int x,y;
public:
Point(int a, int b);
void Print();
};
Point::Point(int a, int b)
{
x = a;
y = b;
}
void Point::Print()
{
cout << "(" << x << "," << y << ")" <<endl;
}
int main()
{
Point a[3] = {Point(1,2), Point(3,4), Point(5,6)};
int i;
for(i = 0; i < 3; i++)
{
a[i].Print();
}
return 0;
}
二,对象指针 ★★★
访问一个对象可以通过对象名访问,也可以通过对象地址访问。对象指针就是用于存放对象地址的变量。
对象指针遵循一般变量指针的各种规则
类名* 对象指针名;
对象指针名->成员名
Circle *c1,c(3);//对象指针和一个对象
c1 = &c;//c1指向对象c
c1->Area();//c.Area()对象访问函数,c1->Area()对象指针访问函数
Circle *c2 = new Circle(3);//用new动态建立
c2->Area();
Circle *c3;
c3->Area();//错误,不能使用没有初始化的对象指针
三,自引用指针this ★★★
this指针主要用在运算符重载和自引用等场合
#include
using namespace std;
class Square
{
private:
double a;
public:
Square(double a);
double Area();
void copy(Square &s);
};
Square::Square(double a)
{
this->a = a;
}
double Square::Area()
{
return a*a;
}
void Square::copy(Square &s)
{
if(this == &s)
return;
*this = s;
}
int main()
{
Square s1(3),s2(5);
cout << "before copy" <<endl;
cout << "s1 area is " << s1.Area() <<endl;
cout << "after copy" <<endl;
s1.copy(s2);
cout << "s1 area is " << s1.Area() <<endl;
}
void Square::copy(Square &s)
{
if(this == &s)
return;
*this = s;
}
Square s1(3),s2(5);
s1.copy(s2);
定义对象s1时通过构造函数将其数据成员初始化为3,当程序执行s1.copy(s2)
时,对象s1调用函数copy(),this指针指向s1自己,在copy函数中判断是不是对象在给自己赋值,如果时就不用了就直接返回,如果不是那就将传给形参s的值赋给this自己就是对象s1
Square::Square(double a)
{
this->a = a;
}
这边this->a = a
是将传给形参的值传给自己的对象成员a,但是都是a,所以要使用this指针告诉编译器是把参数a传给私有成员a
使用this指针应该注意:
1)this指针是一个const指针,不能在程序中修改或给它赋值
2)this指针是一个局部数据,它的作用域仅在一个对象的内部
3)静态成员函数不属于任何一个对象,所i在静态成员函数中没有this指针
C中函数的参数传递有两种方式:按变量值传递和按变量的地址传递
C++中,不仅可以实现C的两种还可以进行引用传递,不仅简单变量可以作为参数进行传递,对象也可以作为参数传递给函数,其方法与传递其他类型的数据一样
#include
#include
using namespace std;
class Point
{
private:
int x,y;
public:
Point(int a, int b);
void Add(Point p);
void Print();
};
Point::Point(int a, int b)
{
x = a;
y = b;
}
void Point::Print()
{
cout << "x:" << x << setw(5) << "y:" << y <<endl;
}
void Point::Add(Point p)
{
p.x = p.x + 1;
p.y = p.y + 1;
}
int main()
{
Point ob(1,2);
cout << "before add:";
ob.Print();
ob.Add(ob);
cout << "after add:";
ob.Print();
return 0;
}
在执行Add函数时,由于作为参数的对象是按值传递的,也就是实参ob将自己的值对应地赋给形参p,在Add函数中对形参的数据成员x,y值的修改结果并没有传回主程序,所以结果是没有变化的
#include
#include
using namespace std;
class Point
{
private:
int x,y;
public:
Point(int a, int b);
void Add(Point *p);
void Print();
};
Point::Point(int a, int b)
{
x = a;
y = b;
}
void Point::Print()
{
cout << "x:" << x << setw(5) << "y:" << y <<endl;
}
void Point::Add(Point *p)
{
p->x = p->x + 1;
p->y = p->y + 1;
}
int main()
{
Point ob(1,2);
cout << "before add:";
ob.Print();
ob.Add(&ob);
cout << "after add:";
ob.Print();
return 0;
}
对象指针作为参数传递给函数,就是传地址,即形参和实参共享的是同一内存单元,因此作为形参的兑现,其数据的改变将影响实参对象,从而实现函数之间的信息传递。此外,**使用对象指针作为参数仅将对象的地址传递给形参,而不进行副本的复制,**可以提高运行效率,减少时空开销
引用相当于变量的别名
1)声明引用的格式:
数据类型 &引用名 = 已定义的变量名;
int &j = i;//表示j是i的引用,变量i和j对应同一内存单元,有一个变了另一个就变了
Point &p = ob;//ob是一个已经定义的Point类的对象,对象p是对象ob的引用
#include
#include
using namespace std;
class Point
{
private:
int x,y;
public:
Point(int a, int b);
void Add(Point &p);
void Print();
};
Point::Point(int a, int b)
{
x = a;
y = b;
}
void Point::Print()
{
cout << "x:" << x << setw(5) << "y:" << y <<endl;
}
void Point::Add(Point &p)
{
p.x = p.x + 1;
p.y = p.y + 1;
}
int main()
{
Point ob(1,2);
cout << "before add:";
ob.Print();
ob.Add(ob);
cout << "after add:";
ob.Print();
return 0;
}
在调用Add(Point &p)
函数时,里面形参类型时引用型,但是传入ob是可以的ob.Add(ob);
,但是如果定义一个Point &yy =ob
,然后往Add(Point p)
传入yy就是错误的。
友元函数不是当前类中的成员函数,**它可以是一个不属于任何一个类的一般函数,也可以是另一个类的成员函数。**当函数被声明为一个类的友元函数后,它就可以通过对象访问类的私有成员和保护成员。
非成员函数作为友元函数,其作为类的友元函数后,就可以通过对象访问封装在类内部的数据。
声明友元函数: friend 函数返回值 函数名(形参表);
例子
#include
using namespace std;
class Complex
{
private:
double real;
double imag;
public:
Complex(double r, double i);
void Print();
friend Complex add(Complex c1, Complex c2);
};
Complex::Complex(double r, double i)
{
real = r;
imag = i;
}
void Complex::Print()
{
cout <<real;
if(imag > 0)
cout << "+";
if(imag != 0)
cout << imag << "i" <<endl;
}
Complex add(Complex c1, Complex c2)
{
c1.real = c1.real + c2.real;
c1.imag = c1.imag + c2.imag;
return c1;
}
int main()
{
Complex com1(1.1, 2.2),com2(3.3, 4.4);
com1 = add(com1, com2);
com1.Print();
return 0;
}
(1)友元函数定义的时候,不用在函数面前家“类名::”,因为它不是该类的成员函数;友元函数没有this指针;调用友元函数时必须在它的实参表中给出要访问的对象。
(2)友元函数可以在类的私有部分进行声明,也可以在类的公有部分进行声明。
(3)当一个函数需要访问多个类时,应该把这个函数同时定义为这些类的友元函数。
(4)友元函数可以使函数能够访问某个类中的私有或保护成员。如果类A的所有成员函数都想访问类B的私有或保护成员,一种方法是将类A的所有成员函数都声明为类B的友元函数,但是这样做显得比较麻烦,且程序也显得冗余。为此,C++提供了友元类。也就是一个类也可以作为另一个类的友元类。若类A声明为类B的友元类,则类A中的每个成员函数都具有访问类B的保护或私有数据成员的特权。
#include
using namespace std;
class Complex
{
private:
double real;
double imag;
public:
Complex(double r, double i);
friend class Vector;
};
class Vector
{
private:
double x,y;
public:
void Change(Complex c);
void Print(Complex c);
};
void Vector::Change(Complex c)
{
x = c.real;
y = c.imag;
}
void Vector::Print(Complex c)
{
cout << "fushu:";
cout << c.real;
if(c.imag > 0)
cout << "+";
cout <<c.imag << "i" <<endl;
cout << "Vector:";
cout << "(" << x << "," << y << ")" <<endl;
}
Complex::Complex(double r, double i)
{
real = r;
imag = i;
}
int main()
{
Complex c(1.1, 2.2);
Vector v;
v.Change(c);
v.Print(c);
return 0;
}
friend class Vector;
友元关系是单向的,且友元关系不能传递