博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++ 类与对象
阅读量:5232 次
发布时间:2019-06-14

本文共 9676 字,大约阅读时间需要 32 分钟。

 类与对象是C与C++的最大区别之一,也是从面向过程转为面向对象的一个转折点

以下分为多部分介绍

1.1 类,结构体的扩展

1.2 公有和私有

1.3 构造函数

1.4 类的继承

1.5 多态

1.1类,结构体的扩展

我们先从熟悉的结构体struct开始,一步一步进入面向对象的世界。

//使用结构体的方法如下

struct Car{    string s_strName;//名字    int s_iWheelNumber;//轮子数量  };
int main(void){    struct Car car;    car.s_strName="Benz";    car.s_iWheelNumber=4;    cout<<"名字:"<
<

在结构体中,我们可以定义变量成员、枚举成员和其他结构体成员,但却没有办法定义函数体,在C++中类的诞生为我们解决了这一问题。

类(class)从使用上可以理解为结构体(struct)的扩展,类中除了可以包含变量、还可以包括函数体等内容。

在上述的struct例子中,改为class,加入了move_forward() 函数、move_back()函数,那么,在实例化类的对象以后,就可以通过car.move_forward(),car.move_back()来调用类中的成员函数了。

class Car{public://这个关键字在1.2介绍    string m_strName;    int m_iWheelNumber;    void move_forward(void);    void move_back(void);};void Car::move_forward(void){    cout<<"前进"<

 

1.2公有和私有

在类中,public关键字下为公有成员、函数。这个关键词修饰下面的内容在类的外部可以调用 。private下则为私有,仅为类的内部函数才能使用。

把1.1中的例子修改成下面,在实例化后,我们不能通过car.m_strName访问 m_strName这个成员,因为它在private关键词下,类外不能访问,那么要如何操作呢?请看例子

#include
#include
using namespace std;class Car{public: void setName(string _name) { m_strName = _name; } string getName(void) { return m_strName; } void move_forward(void); void move_back(void);private: string m_strName; int m_iWheelNumber;};void Car::move_forward(void){ cout<<"前进"<

虽然m_strName为pravite不能在外部直接操作,但可以使用类中的方法setName()可以改变m_strName的值,getName()方法可以返回m_strName的值,把类中的成员封装起来通过函数调用,可以防止误操作改变成员,是面向对象的基本思想之一。

1.3构造函数与析构函数

C++的类作为结构体第三大升级,类具有构造函数析构函数。分别在类的创建(实例化)和消除的时候自动调用。

1.3.1构造函数

构造函数名字与类名相同,在实例化的时候自动调用,一般用于对类中的成员赋予初始值。

继续使用上面的例子改造

class Car{public:    Car(){m_strName ="Benz",m_iWheelNumber=4;};//构造函数    void move_forward(void);    void move_back(void);    void setName(string _name)    {    m_strName = _name;    }    string getName(void)    {    return m_strName;    }private:    string m_strName;    int m_iWheelNumber;};void Car::move_forward(void){    cout<<"前进"<

*注意:

  • 即是没有写构造函数,C++也会自动为类添加一个空的构造函数

构造函数可以带有参数,参数还可以有默认值:

  Car(string _name,int _wheelnumber=4){m_strName =_name,m_iWheelNumber=_wheelnumber;};//构造函数

基本构造函数还可以重载(关于重载在多态中详细介绍),一个方法可以有多个基本构造函数,将上面两种构造函数放在同一个类中:

class Car{public:    Car(){m_strName ="Benz",m_iWheelNumber=4;cout<<"1"<

 

汽车的轮子是固定的,所以我们可能会使用const来修饰(表示是一个常量,不可改变),那么,如何对const类型赋初值呢?如果直接在构造函数内赋值则提示不可变的参数,这时候需要用到初始化列表  

 

初始化列表是在构造函数的(){}之间插入:类内string变量("xxx"),类内整形变量(4)的方式,例子如下

class Car{public:    Car():m_strName("Benz"),m_iWheelNumber(4){}//构造函数+初始化列表    void move_forward(void);    void move_back(void);    void setName(string _name)    {        m_strName = _name;    }    string getName(void)    {        return m_strName;    }    const int getWheelNum(void)    {        return m_iWheelNumber;    }private:    string m_strName;    const int m_iWheelNumber;};

1.3.2 拷贝构造函数

除了基本构造函数,类中还默认存在一个拷贝构造函数,也就是拷贝的时候调用的构造函数,比如Car c1; Car c2=c1或者Car c2(c1);则会调用拷贝构造函数。

#include
#include
using namespace std;class Car{public: Car(){m_strName ="Benz",m_iWheelNumber=4;cout<<"1"<

 

*注意:

  • 拷贝构造函数如果没有写系统会自动分配一个默认拷贝构造函数,默认的拷贝构造函数会把成员全部拷贝。
  • 对象在函数传参的时候会发生拷贝,此时也会触发拷贝构造函数。

1.3.3析构函数

  析构函数是对象在消除的时候自动调用的函数,其名字与类相同,并在前加“~”符号,当用户没有定义析构函数时,C++会自动生成一个空的析构函数。

  上述Car的例子,编写析构函数:

class Car{public:  Car(){m_strName ="Benz",m_iWheelNumber=4;cout<<"1"<

  *注意:

  • 析构函数必须无输入参数,且不能重载。

 1.4 类的继承

  面向对象的三大特性之一,类可以继承

  什么是继承?

  例如,Car(汽车)类中包括1、轮子数量 2、时速 两个成员 

     BMW(宝马)类中包括 1、轮子数量 2、时速 3、外形 三个成员。

  那么我们说 宝马(类) 是一种 汽车(类)

  为了方便,我们不必把宝马类的全部成员重写一遍,只需要从汽车类中继承。下面的例子介绍如何定义宝马类。

1.4.1公有继承

  使用public关键字继承,Car是基类,也叫父类,BMW是Car中继承的类,叫派生类,也叫子类

  *在公有继承中,父类public的成员也成为子类public成员

           父类protected的成员成为子类protected成员

           父类private的成员不会出现在子类中(不发生继承)

  也就是说 ,因为BMW继承了Car类,Car类中protected关键字下的m_iWheelNumber和m_iSpeed会自动复制到BMW类中:

  下面是一个公有继承的例子  

class Car{public:    Car(){m_iWheelNumber=4;};//普通构造函数    void move_forward(void);protected://可以发生继承的private形式    int m_iWheelNumber;    int m_iSpeed;private://private中的成员不会发生继承    string m_strName;};class BMW:public Car//宝马类继承小车类{public:    BMW(){}protected:    string m_strLook;};

 

在上面这个例子中 ,BMW类继承了Car类,那么,BMW类中除了m_strLook成员外,还具有m_iWheelNumber和m_iSpeed成员,实际它的成员如下

class BMW:public Car{public:    BMW(){}    void move_forward(void);//继承来的函数成员,不用写,默认存在protected:    int m_iWheelNumber;//继承来的成员,不用写,默认存在    int m_iSpeed;//继承来的成员,不用写,默认存在    string m_strLook;private://空};

 

1.4.2 保护继承

  *在公有继承中,父类public的成员也成为子类protected成员

           父类protected的成员成为子类protected成员

           父类private的成员不会出现在子类中(不发生继承)

  上面的例子改为保护继承,那么实际上BMW类中从Car继承来的成员都移到了protected下:

class BMW:protected Car{public:    BMW(){}protected:    void move_forward(void);//继承来的函数成员,不用写,默认存在 父类public->子类protected    int m_iWheelNumber;//继承来的成员,不用写,默认存在    int m_iSpeed;//继承来的成员,不用写,默认存在    string m_strLook;private://空};

 

1.4.2 私有继承

  *在公有继承中,父类public的成员也成为子类private成员

           父类protected的成员成为子类private成员

           父类private的成员不会出现在子类中(不发生继承)

上面的例子改为私有继承,那么实际上BMW类中从Car继承来的成员都移到了private下:

class BMW:private Car{public:    BMW(){}protected://空private:    void move_forward(void);//继承来的函数成员,不用写,默认存在 父类public->子类private    int m_iWheelNumber;//继承来的成员,不用写,默认存在,父类protected->子类private    int m_iSpeed;//继承来的成员,不用写,默认存在,父类protected->子类private    string m_strLook;};

1.4.2 多继承

  一个子类从多个父类中发生继承,称为多继承

  举个例子:

  有Car(小车)和TOY(玩具)两个类

  其中,Car类有  1、轮子数量 2、速度 两个成员

     TOY类有 1、尺寸  2、颜色 两个成员

  现在我们要新建一个玩具车类,它需要有Car类和TOY类的所有成员,那么我们可以这样写:

class Car//父类1{public:    Car(){m_iWheelNumber=4;};//普通构造函数    void move_forward(void);protected:    int m_iWheelNumber;    int m_iSpeed;};class Toy//父类2{public:    Toy(){};protected:    int m_iSize;    int m_iColor;  };class ToyCar:public Car,publicToy{pulic:    TopCar(){};};//同样的,ToyCar类默认拥有了Car 和 Toy的public和protected的成员class ToyCar:public Car,publicToy{pulic:    TopCar(){};protected:    int m_iWheelNumber;    int m_iSpeed;    int m_iSize;    int m_iColor; };

1.5 多态

  多态,表示同名的参数产生不同的结果,他们包括

  一般多态:

  • 参数多态:(模板
  • 包含多态:不同类的同一个函数发生不同的结果(覆盖

  特殊多态:

  • 重载多态:表示同一个函数不同调用发生的不同结果(重载
  • 强制多态:类型的强制转换(强转

  上述这些名词是没有意义的,我们真正是要学会使用他们,下面我们展示它们的意义和实现方法

1.5.1 重载

1.5.1 函数重载

  我们继续以Car为例子:Car具有两个成员  名字m_strName 和 速度m_iSpeed  ,并且设置了默认前进函数move_forward(void),调用它会给m_iSpeed默认赋值100的。但是为了能够准确的设置前进速度,我们另外写了一个同名函数move_forward(int speed),这两个同名函数互为重载。当我们调用car.move_forward()会调用第一个,当传入参数时候,调用第二个前进函数,并赋予传入参数的m_iSpeed。

class Car{public:    Car(){m_strName = "BMW";m_iSpeed=0;};//默认构造函数    Car(string name,int speed){m_strName =name;m_iSpeed = speed;}//有参构造函数 与 默认构造函数互为重载    void move_forward(void);    void move_forward(int speed);//输入前进速度的move_forward函数    int getSpeed(void);    string getName(void);private:    string m_strName;    int m_iSpeed;//行驶速度};void Car::move_forward(void){    m_iSpeed = 100;};void Car::move_forward(int speed){    m_iSpeed = speed;}int Car::getSpeed(void){    return m_iSpeed;}string Car::getName (void){    return m_strName;}int main(void){    Car car1;//自动使用默认构造函数Car(), car1是宝马BMW    Car car2("Lambo",0);//自动使用有参的构造函数Car(string name,int speed) car2是兰博基尼        cout<<"car1 name:"<
<

 

1.5.2 运算符重载

运算符重载意思是把常用的运算符赋予新的功能,比如“+、-、*、/、++、--、[] ”等等。当运算符被重载后,特定的情况下会赋予运算符不一样功能

比如,继续以Car类为例子,当一个car的对象与另一个car的对象相加的时候,我们希望他们的轮子数量相加,并且名字改为变形金刚。car3=car1+car2,car3就是变形金刚,且轮子数为8,但类中是不支持加法的,那我们就需要在Car类中重载运算符"+"

在接触运算符重载之前,我们现需要了解一个新关键词:this

1.5.2.2 关键词this

 

直接看例子

(此处插入Car类程序)

this实际是一个指针 ,它指向这个类实例化成对象后的第一个地址。

例如 Car c1;此时this指向c1的首地址。

那么,this和运算符重载有什么关系呢?这里需要补充说明,在所有的类内函数中,默认传递参数 this指针也就是说,Car类实际上是这样的

(此处插入程序)

在了解了this以后,就可以真正进入运算符重载了:

 

1.5.2.1 关键词operator

 operator 是重载运算符的关键词,当我们想重载哪一个运算符,我们使用operator后面紧跟符号

下面的例子,是重载“+”,用于一个Car类的对象与另一个Car类的对象相加。当大家看了下面两个问题就能理解运算符重载的操作了。

问:Car operator+(Car &car);传入的参数有几个 ? 答:有两个,第一个是C++默认传参this指针,第二个是写出来的Car &car
问:car3=car1+car2;这句话相当于什么意思? 答:相当于car3=car1.operatro+(car2);
#include
#include
using namespace std;class Car{public: Car(){m_strName = "BMW";m_iWheel=4;};//默认构造函数 Car(string name,int wheel){m_strName =name;m_iWheel = wheel;}//有参构造函数 与 默认构造函数互为重载 int getWheel(void); string getName(void); void setWheel(int wheel); void setName(string name); Car operator+(Car &car);private: string m_strName; int m_iWheel;};int Car::getWheel(void){ return m_iWheel;}string Car::getName (void){ return m_strName;}void Car::setName(string name){ m_strName=name;}void Car::setWheel(int wheel){ m_iWheel = wheel;}Car Car::operator+(Car &car){ Car out; out.setName("变形金刚"); out.setWheel(this->m_iWheel+car.getWheel()); return out;}int main(void){ Car car1("BMW",4);//自动使用默认构造函数Car(), car1是宝马BMW Car car2("Lambo",4);//自动使用有参的构造函数Car(string name,int speed) car2是兰博基尼 Car car3; car3=car1+car2; cout<<"car1:"<
<<" "<
<

 

1.5.2 覆盖

  覆盖、要从虚函数说起,在类的成员函数前加virtual关键字表示这是一个虚函数,当这个类发生继承的时候,子类可以重写虚函数,重写后父类的虚函数被覆盖,也就是父类的同名方法不会再被调用。

  例如,在上述的Car类中加入一个漂移的函数virtual void drift(void);但是不是所有类型的小车都能漂移,比如宝马可以漂移,大众漂移会翻车。那么我们就在Car的子类宝马BMW中重新实现漂移这个函数。

class Car{public:    Car(string name ,int size=4):m_strName(name),m_iSize(size){cout<<"Car()"<
getName()<
drift(); delete b; b=NULL; system("pause"); return 0;}

上面这个例子打印如下

Car() 

BMW()

名字:BMW

漂移~//关键词virtual使得父类drift()已经被覆盖了,所以打印的是子类的drift();

~BMW()

~Car()//析构函数作为虚函数是例外,它的父类函数不会被覆盖,会一起被调用

*注意

  • virtual声明的成员函数在子类中仍然是virtual ,即是没有人为写上。
  •      virtual声明的析构函数不会发生覆盖,而是在对象销毁时,子类的析构函数被调用后、父类的析构函数接着调用

 

1.5.3 模板

模板分为类模板和函数模板,他们的使用方法基本一样,在接触这一章节中,我们会接触到一个有趣的关键字 template,typename T代表类型:

template

 

例子:

以下实现的a、b数据交换,可以输入任意的类型。

template 
void swapnum(T &a,T &b){ T c; c=a; a=b; b=c;}
int main(){    float a=1.1;    float b=2.2;    int a1=1;    int b1=2;    swapnum
(a,b); cout<<"a:"<
<
<<"b:"<
<

 

转载于:https://www.cnblogs.com/HongYi-Liang/p/7092005.html

你可能感兴趣的文章
[转载]树、森林和二叉树的转换
查看>>
软件测试-----Graph Coverage作业
查看>>
django ORM创建数据库方法
查看>>
创建Oracle synonym 详解
查看>>
php7 新特性整理
查看>>
RabbitMQ、Redis、Memcache、SQLAlchemy
查看>>
linux查看端口占用
查看>>
Sql常见面试题 受用了
查看>>
知识不是来炫耀的,而是来分享的-----现在的人们却…似乎开始变味了…
查看>>
CSS背景颜色、背景图片、平铺、定位、固定
查看>>
口胡:[HNOI2011]数学作业
查看>>
我的第一个python web开发框架(29)——定制ORM(五)
查看>>
中国剩余定理
查看>>
基础笔记一
查看>>
uva 10137 The trip
查看>>
Count Numbers
查看>>
编写高质量代码改善C#程序的157个建议——建议110:用类来代替enum
查看>>
网卡bond技术
查看>>
UITabbarController的UITabbarItem(例:"我的")点击时,判断是否登录
查看>>
UNIX基础知识之输入和输出
查看>>