浅谈C++
1.类的定义
为了在程序中创建对象,必须首先定义类.c++语言用保留字class定义一个类,一般形式为:
class 类名
{
• public:
• <共有数据和函数>
• protected:
• <保护数据和函数>
• private:
• <私有数据和函数>
};
#值得注意的是,右花括号后的分号";"作为类声明的结束标志是不能漏掉的.类中定义的数据和函数分别称为数据成员和成员函数.
2.类成员的访问控制
关键字public,protected和private均用于控制类中的成员在程序中的可访问性,关键字public,protected和private以后的成员的访问权限分别是共有,保护和私有的.所有成员默认定义为private的,当为了提高程序的可读性,不主张使用这种默认定义方式.
3.对象
类是一种程序员自定义的数据类型,称为类类型,程序员可以使用这个新类型在程序中声明新的变量,具有类类型的变量称为对象.
3.1声明对象的一般格式
<类名> <对象名表>;
其中,<类名>是所定义的对象所属类的名字.<对象名表>中可以是一般的对象名,也可以是指向对象的指针名或者引用名,还可以是对象数组名.指向对象的指针称为对象指针,对象的引用称为对象引用.
例如,声明类Point的对象如下所示:
Point p1,p2,*pdate,p[3],&rp=p1;
其中,Point是类名,p1和p2是两个一般的对象名,pdate是指向类Point的对象指针名,p[3]是对象数组,该数组是具有3个元素的一维数组,每个数组元素是类Point的一个对象,rp是对象引用名,它被初始化后,rp是对象p1的引用.
3.2构造函数和析构函数
3.2.1构造函数
构造函数是一种特殊的成员函数,对象的创建和初始化工作可以由它来完成.其格式如下:
<类名>::<类名>(形参表)
{
<函数体>
}
构造函数应该被声明为共有函数,因为它是在创建对象的时候被自动调用.构造函数有如下特点:
它的函数名与类名相同
它可以重载
不能指定返回类型,即使是void类型也不可以
它不能被显示调用,在创建对象的时候被自动调用
3.2.1.1默认构造函数
默认构造函数就是无参数的构造函数.既可以是自己定义的,也可是是编译系统自动生成的.
当没有为一个类定义任何的构造函数的情况下,编译系统就会自动生成一个无参数,空函数体的默认构造函数.其格式如下:
<类名>::<类名>(){}
3.2.2析构函数
析构函数也是一种特殊的成员函数,它的作用是在对象消失时执行一项清理任务,例如,可以用来释放由构造函数分配的内存等.其格式如下:
<类名>::~<类名>(){}
构造函数也只能被声明为共有函数,因为它是在释放对象的时候被自动调用.析构函数有如下特点:
析构函数的名字同类名,与构造函数名的区别在于析构函数名的前面加"~",表明它的功能与构造函数的功能相反.
析构函数没有参数,不能重置,一个类中只定义一个析构函数
不能指定返回类型,即使是void类型也不可以
析构函数在释放一个对象时候被自动调用,与构造函数 不同的是,它能被显示调用,当不提倡
3.2.2.1默认析构函数
如果一个类中没有定义析构函数时,系统将自动生成一个默认构造函数,其格式如下:
<类名>::~<类名>(){}
3.3拷贝构造函数
拷贝构造函数是一种特殊的构造函数,它的作用是用一个已经存在的对象去初始化另一个对象,为了保证,所引用的对象不被修改,通常把引用参数声明为const参数.其格式如下:
<类名>::<类名>(const <类名>&<对象名>)
{
<函数体>
}
拷贝构造函数具有一般构造函数的特性,特点如下:
拷贝构造函数名字与类名相同,并且不能指定返回类型
拷贝构造函数只有一个参数,并且该参数是该类的引用
它不能被显示调用,在以下三种情况都会被自动调用当用一个类的对象去初始化另一个对象时当函数的形参是类的对象,进行形参和实参的结合时当函数的返回值是类的对象,函数执行完成返回调用者时
3.3.1默认拷贝构造函数
如果一个类中没有定义拷贝构造函数,则系统自动生成一个默认拷贝构造函数.该函数的功能是将已知对象的所有数据成员的值拷贝给对应的对象的所有数据成员.
3.4类的静态成员
每创建一个对象时,系统就为该对象分配一块内存单元来存放类中的所有数据的数据成员.这样各个对象的数据成员可以分别存放,互补相干.但在某些应用中,需要程序中属于某个类的所有对象共享某个数据.虽然可以将所要共享的数据说明为全局变量,但这种解决办法将破坏数据的封装性.较好的解决办法是将所要共享的数据说明为类的静态成员.静态成员是指声明为static的类成员,包括静态数据成员和静态成员函数,在类的范围内所有对象共享该数据.
3.4.1静态数据成员
静态数据成员不属于任何对象,它不因对象的建立而产生,也不因对象的析构而删除,它是类定义的一部分,所以使用静态成员不会破坏类的隐蔽性.类中的静态数据成员布偶同与一般的静态变量,也不同于其他类的数据成员.它在程序开始运行时创建而不是在对象创建时创建.它所占空间的回收也不是在析构函数时进行而是在程序结束时进行.
3.4.2静态数据成员的初始化
必须对静态数据成员进行初始化,因为只有这时编译器才会为静态成员分配一个具体的空间.静态数据成员的初始化与一般数据成员不同,它的初始化不能在构造函数中进行.静态数据成员初始化格式为:
<数据类型><类名>::<静态数据成员名>=<初始值>;
#这里的作用域运算符"::"用来说明静态数据成员所属类.
3.5类的友元
有时候,需要普通函数直接访问一个类的保护或私有数据成员.例如要求两点之间的距离,判断两个矩形的面积是否相等,这需要访问前面点类中的点的坐标X和Y,矩形类中的面积area.友元是c++提供给外部的类或函数访问类的私有成员和保护成员的另一种途径,它提供在不同类的成员之间,类的成员函数与一般函数之间进行数据共享的机制.友元可以是一个函数,称为友元函数,也可以是一个类,称为友元类.
3.5.1友元函数
在类里声明一个普通函数,加上关键字friend,就成了该类的友元函数,它可以访问该类的一切成员.其原型为:
friend <类型><友元函数名>(<参数表>);
#友元函数声明的位置可以在类的任何地方,既可以在共有区,也可以在保护区,意义完全相同.友元函数的实现则在类的外部,一般与类的成员函数定义放在一起.
例-利用友元函数求两个点之间的距离:
#include
using namespace std;
class Point {
public:
//构造函数
Point(double x, double y);
//析构函数
~Point();
//计算距离的友元函数
friend double distance(Point& a, Point& b);
private: //私有成员变量
double X, Y;
};
//构造函数的实现
Point::Point(double x, double y) {
X = x;
Y = y;
}
//析构函数的实现
Point::~Point() {
cout << “析构函数被调用~”;
}
//在类的外部对友元函数进行实现
double distance(Point &a, Point &b) { //两个参数都是对Point类的对象的引用
double len;
len = sqrt(pow((a.X - b.X), 2) + pow((a.Y - b.Y), 2));
cout << “两点之间的距离为:” << len << endl;
return len;
}
int main() {
//创建两个点对象
Point A(0, 0), B(3, 4);
distance(A, B); //调用友元函数,计算两个点之间的距离
return 0;
}
取的两个分别为(0,0)和(3,4),计算出两点之间的距离为5,运行结果如下:
3.5.2友元类
除了函数之外,一个类也可以被声明为另一个类的友元,该类被称为友元类.假设有类A和类B,若在类B的定义中将类A声明为友元,那么,类A被称作类B的友元类,它所有的成员函数都可以访问类B中的任意成员.友元类的声明格式为:
friend class<类名>;
继承机制
4.1继承和派生的基本概念
通过继承机制可以利用已有的数据类型来定义新的数据类型.根据一个类创建一个新类的过程称为继承,也称派生.
4.1.1继承的种类
在c++语言中,一个派生类既可以从一个基类派生,也可以从多个基类派生.从一个基类派生的继承被称为单继承,单继承形成的类层次是一个倒挂的树,从多个基类派生类的继承被称为多继承,多继承形成的类层次是一个有向无环图.
4.1.2二义性
一般地讲,在派生类中对基类成员的访问是唯一的.但是,在有多继承的情况下,可能会造成派生类对基类成员访问的不唯一性,即二义性.
1.调用不同基类的相同成员时可能出现二义性.
2.访问共同基类的成员时可能出现二义性.(如果一个派生类从多个基类派生而来,而这些基类又有一个共同的基类,则在这个派生类中访问这个共同基类中的成员时可能会产生二义性.)
例:
#include
using namespace std;
//基类1
class Base1{
public:
void show(){
cout<<“这是基类1的show函数”<<endl;
}
};
//基类2
class Base2{
public:
void show(){
cout<<“这是基类2的show函数”<<endl;
};
};
//派生类同时继承自基类1和基类2
class Child public:Base1,public:Base2{
}
int main(){
//创建一个派生类的对象
Child demo;
demo.show(); //派生类的子对象调用show方法
return 0;
}
程序运行结果如下:
由于同时继承的两个基类中都有show方法,因此派生类的子对象关于要调用哪个show方法的时候指向不明确,程序报错.
解决办法:
可以使用作用域运算符,指明要调用的show方法来自继承的哪一个类,例(调用Base1的show方法):
demo.Base1::show();
4.1.3支配原则
类X中的名字N支配类Y中同名的名字N,是指类X以类Y为它的一个基类,这称为支配原则.如果一个名字支配另一个名字,则二者之间不存在二义性,当选择该名字时,使用支配者的名字即可.
例(利用支配原则解决上述的二义性问题):
如果在派生类中定义一个自己的show方法,那么就出现了与基类中同名的情况,那么派生类的对象在调用show方法试,会使用支配者的show方法(即派生类自己的show方法):
class Child : public Base1, public Base2 {
public:
void show() {
cout << “这是派生类的show方法” << endl;
}
};
程序同样正常运行:
4.1.4虚基类
引进虚基类的目的是为了解决二义性问题,使得公共基类在它的派生类对象中只产生一个基类子对象.虚基类说明的格式如下:
virtual <继承方式> <基类名>
其中,virtual是说明虚基类的关键字.虚基类的说明是用在定义派生类时,写在派生类名的后面.
4.1.5多继承机制下构造函数的调用顺序
首先基类构造函数被调用
子对象所在类的构造函数次之
最后执行派生类构造函数
#多继承机制下析构函数的调用顺序与之相反
4.2程序设计
定义一个点类(Point).矩形类(Rectangle)和立方体类(Cube)的层次结构.矩形包括长度和宽度两个新数据成员,矩形的位置从点类继承.立方体类由长度,宽度和高度构成.要求各类提供支持初始化的构造函数和显示自己成员的成员函数.编写主函数,测试这个层次结构,输出立方体类的相关信息.
#include
using namespace std;
//定义一个点类
class Point {
public:
static int ObjNum; //定义一个静态成员变量 用于记录创建的对象的个数
Point(double x, double y); //构造函数
~Point(); //析构函数
void show(); //用于显示成员数据的函数
private:
double X, Y;//点的坐标x和y的值
};
//点类的成员函数的实现
Point::Point(double x, double y) {
cout << “点类的构造函数被调用” << endl;
X = x;
Y = y;
ObjNum ++; //如果构造函数调用成功,说明对象创建成功,对象数量加1
cout << “当前的对象数量为:” << Point::ObjNum << endl;
}
//点类的析构函数
Point::~Point() {
cout << “点类的析构函数被调用” << endl;
ObjNum–; //如果析构函数调用成功,说明对象销毁成功,对象数量减1
cout << “当前的对象数量为:” << Point::ObjNum << endl;
}
void Point::show() {
cout << “我的坐标位置是:(” << X << “,” << Y << “)” << endl;
}
//定义一个矩形类 继承自点类
class Rectangle :public Point
{
public:
//构造函数
Rectangle(double x, double y, double length, double width);
//析构函数
~Rectangle() {
cout << “矩形类的析构函数被调用” << endl;
cout << “当前的对象数量为:” << Point::ObjNum << endl;
}
//矩形类的show函数
void show();
double area;//(表)面积
private:
double Length, Width;//矩形的长度 宽度
};
//矩形类构造函数的实现
Rectangle::Rectangle(double x, double y, double length, double width) :Point(x,y) { //Point(x,y):点类的数据成员的初始化,同时也会调用点类的构造函数
cout << “矩形类的构造函数被调用” << endl;
Length = length;
Width = width;
area = length * width;
cout << “当前的对象数量为:” << Point::ObjNum << endl;
}
//矩形类show方法的实现
void Rectangle::show() {
Point::show();
cout << “我的长度和宽度分别是:” << Length << “,” << Width << endl;
cout << “我的(表)面积是:” << area << endl;
}
//定义一个立方体类 继承自矩形类
class Cube :public Rectangle {
public:
//立方体类的构造函数
Cube(double x, double y, double length, double width, double height);
//立方体类的析构函数
~Cube() {
cout << “立方体类的析构函数被调用” << endl;
}
//立方体类的show函数
void show();
private:
double Height, V;
};
//立方体类构造函数的实现
Cube::Cube(double x, double y, double length, double width, double height) :Rectangle( x, y, length, width) {
cout << “立方体类的构造函数被调用” << endl;
Height = height;
//因为在Rectangle中,area是共有成员变量,所以通过public的方式下来能够有权限访问到
area = (length * width + length * height + width + height) * 2; //重写表面积的计算方法
V = length * width * height; //体积
cout << “当前的对象数量为:” << Point::ObjNum << endl;
}
//立方体类的show函数
void Cube::show() {
Rectangle::show(); //调用矩形类的show方法
cout << “我的高度是” << Height << endl;
cout << “我的体积是” << V << endl;
}
//初始化静态数据成员 (不能在main函数里面初始化)
int Point::ObjNum = 0;
int main() {
//初始化以立方体类
double x = 0, y = 0, length = 10, width = 10, height = 10;
Cube demoCube(x, y, length, width, height);
demoCube.show();
//初始化一个点类
Point demoPoint(x,y);
return 0;
}
程序运行结果如下:
多态性和虚函数
5.1静态联编和动态联编
多态性就是统一符号或者名字在不同情况下具有不同解释现象,即是指同一个函数的多种形态.c++可以支持两种多态性,编译时的多态性和运行时的多态性.
关于多态性的讲解可以查看我个人博客系统中的多态性和虚函数
c++基础必备,这是我当初我c++复习的笔记,觉得好的话可以一键三连哦~