面向对象编程学习笔记(C++)

参考书:c/c++程序设计教程,龚沛增、杨志强 主编
本文主要内容架构如下图:


面向对象(c++)

1 概述

面向对象编程(Object Oriented Programming),面向对象的思想就是要面对现实世界的实体,以对象为基本单位,分析、设计和实现一个系统。
基本概念:

  1. 对象
    现实世界中的对象是各种各样的实体,可以是具体的或抽象的事物。每个对象皆有自己的特征、行为和作用在该对象上的活动。

  2. 许多对象具有相似的性质,执行相同的操作,称之为一类对象。类是创建对象的模板,对象则是类的实例。
  3. 消息
    在面向对象的程序中,程序执行是对象之间传递消息完成的,消息传递一般由 接收消息的对象、所要触发的方法和执行此方法需要的参数组成的。
  4. 方法
    方法是某个对象接受了消息后采取的一系列操作的描述。

2 类和对象

本节内容如下:


类和对象

2.1 类的定义

类定义的一般形式如下:

class 类名
  {
    public :
      成员函数或数据的说明;
    private :
      成员函数或数据的说明;
    protected :
      成员函数或数据的说明;
  };
  各成员函数的实现;

--类的定义包括说明和实现两部分。如果成员函数在说明部分已经给出定义,则在实现部分可以省略;
--类的成员包含数据成员和函数成员,自身类的对象不能作为自己的成员;
--private,public,protected规定了成员如何被访问,缺省时为private;
--不涉及继承时,protected 与private用法相同;
--类的数据成员不能在声明时进行初始化,只能通过成员函数实现;
--在类体外定义成员函数时,格式为:

类名::函数名(参数)
{
  函数实现
};

2.2 对象的定义

定义对象
类名 对象名;
如:

class staff  {......};
 staff s,*p,s1[5];
//staff为已经定义的类,s是一个对象,p是指向对象的指针,s1为对象数组

对象的访问

  1. 一般对象成员:
    对象名.数据成员名
    对象名.函数成员名(参数)
  2. 指针对象成员:
    对象指针->数据成员
    对象指针->函数成员(参数)

    对象指针).数据成员
    对象指针).函数成员(参数)

2.3 对象初始化

  1. 构造函数
    --与类名相同,不指定函数类型;
    --属于成员函数,可以在类体内或体外实现;
    --可以重载;
    --在创建对象时自动调用,不能在程序中直接调用,多用于初始化;
    --用户未定义时,系统会自动生成缺省构造函数;
    --用户定义了构造函数,系统不会生成缺省构造函数;
    --若定义一个静态对象而没有初始化时,编译器会将所有数据成员初始化为0.

  2. 析构函数
    --在程序结束或使用对象的函数结束前会自动调用,用于释放对象;
    --析构函数名是在类名前加~,无参数,不需指定类型;
    --属于成员函数,可以在类体内或体外定义;
    --不能重载;
    --可以由系统自动调用,也可以在程序中调用;
    --用户未定义时,系统会自动生成空的缺省析构函数。

  3. 拷贝初始化构造函数
    --用一个已知的对象来初始化一个被创建的同类对象;
    --函数名与类名相同,只有一个参数,且该参数是对同类对象的引用;
    --如果用户没有定义拷贝初始化构造函数,则系统会自动生成,并将参数对象的全部数据值拷贝给正在创建的对象。

2.4 常对象和常成员

声明常对象:

const 类名 对象名;

常对象中所有数据成员的值只能在构造函数中初始化,不能被修改。

常函数成员
声明常函数成员:

类型说明符 函数名(参数) const
{......};

--只有常成员函数可以操作常对象;
--修改数据对象的函数不能声明为常函数;

常数据成员
声明常数据成员:

const 数据类型 变量名;

--常数据成员不能被任何函数改动;
--构造函数只能通过初始化列表对其初始化,如:

class circle
{
  private:
    double x,y;
    const double r;
  public:
    circle(double x1, double y1,double r1):r(re)
      {
        x=x1;y=y1;
      }
 }

2.5 静态成员

希望一个成员能供所有对象共享时,可以设为静态成员。
静态数据成员
--静态数据成员声明时在前面加static;
--静态数据成员是类的成员,不是对象的成员;
--初始化在类体外进行,不加static和访问权限控制符。

//初始化格式:
数据类型  类名::静态数据成员=值;
//引用格式:
类名:静态数据成员名

静态函数成员
--声明时在前面加static;
--静态成员函数中可以直接引用静态数据成员,对于非静态数据成员,要通过对象引用;
--除了可以像其他成员函数一样引用,还可以如下引用:

类名::成员函数名(参数)

2.6 友元函数

友元函数提供了一种非成员函数访问类的私有成员的方法,在一定情况下可以提高程序的执行效率,但破坏了类的封装和隐藏性。
--友元函数不是成员函数,但要在类体内说明;
--友元函数说明时,要在前面加friend,实现时不许加friend;
--友元函数可以通过对象的方式访问类中的私有成员。

3 派生和继承

继承是软件可重用性的一种形式,新类通过这种方式,从现有的类中吸收其属性和行为,并对其覆盖和改写,产生新类所需要的功能。
本节内容如下:


继承和派生

3.1 派生

通过继承机制,可以应用现有类来定义新类。已经存在的类称为基类或父类,由已存在的类派生的新类叫派生类或子类。
派生类拥有基类的成员,也可以定义新成员。

3.2 继承

一个派生类可以由一个基类派生,称为单继承;也可以由多个基类派生,称为多继承。
单继承

  1. 派生类的定义

     class 派生类名:继承方式  基类名
     {
       派生类新成员定义
     };
    
  2. 派生类的继承方式
    不同的继承方式中,派生类对基类成员的访问权限不同,如下:

基类 私有成员 公有成员 保护成员
私有派生 不可访问 私有成员 私有成员
公有派生 不可访问 公有成员 保护成员
保护派生 不可访问 保护成员 保护成员

--不论何种继承方式,基类的私有成员均不可被派生类及其成员函数访问;
--私有继承时,基类的公有成员和保护成员均成为派生类的私有成员;
--公有继承时,基类的公有成员和保护成员类型不变被继承到派生类中;
--保护继承时,基类的公有成员和保护成员均成为基类的保护成员;
--若基类的公用成员访问了基类的私有成员,则在派生类中不能使用,需要通过基类的方式调用。

  1. 派生类的构造函数
    定义派生类的构造函数时,除了对自己的数据成员初始化,还需调用基类的构造函数。如果派生类中的成员含有基类对象,则还应该对基类对象进行初始化,例如:

     派生类构造函数(初始化总参数表):基类构造函数(参数表1),基类对象名(参数表2)
     {
       派生类中数据成员初始化
     }
    

--调用顺序:基类构造函数——基类对象构造函数——派生类构造函数;
--基类中有缺省的构造函数或未定义时,派生类构造函数可以省略调用基类构造函数;
--若基类只包含有参数的构造函数,则在派生类中一定要调用。

#include <iostream>
using   namespace   std   ;
class circle
{
  private:
    double x,y,r;
  public:
    void print() //const
    {
        cout<<"圆心:"<<x<<","<<y<<endl;
        cout<<"半径:"<<r<<endl; 
        cout<<"n:"<<n<<endl; 
    }
    circle(double x1,double y1, double r1,int i1)
    {
        x=x1;y=y1;r=r1;i=i1;
    }
    circle()
    {
    };

class c:public circle
{
  private:
    int cd;
  public :
    c (int cd1,double x1,double y1, double r1,int i1 ):circle( x1, y1,  r1, i1)
//注意基类构造函数中没有数据类型
    {
        cd=cd1;
    }
    void print()
    {
        circle::print();
//基类中的print函数访问了基类私有成员,在派生类中不能直接使用,需要通过基类调用
        cout<<"cd:"<<cd<<endl;
    }
};

int main(int argc, char** argv) {
  circle p1(0,0,2,1);
  c c1(1,2,3,4,5);
  p1.print();
  c1.print();
};

运行结果:


image.png
  1. 派生类析构函数
    格式:

     ~派生类名()
     {......};
    

系统先执行派生类的析构函数,再执行基类的析构函数。

4 多态性

多态性是指同一个操作对于不同的对象会产生不同的反应。函数重载和运算符重载属于静态重载,建立在虚函数上的多态性属于动态重载。
本节内容如下:


多态性

4.1 函数重载

函数重载是指同一个函数名有不同的定义,定义重载函数时,要求参数至少有一个类型不同,或者个数不同,对返回值无要求。

4.2 运算符重载

  1. 运算符重载原则
    遵循函数重载的原则;
    不能改变运算符的优先级和结合性,不改变其语法结构。

  2. 重载为类的成员函数
    定义形式:
    类名 operetor 运算符(参数表)
    调用形式:
    对象1 运算符 对象2
    例如(程序待完善):

     class Str    
     {
       private:
         char *s;
       ......
       public:
         Str operator + (Str s1)
         //将+号重载为字符串拼接
         {
           strcat(s, s1.s);
           Str result(s);
           return result;
         }
     };
     void main()
     {
       Str s1,s2,s3;
       s3=s1+s2;
     }
    
  3. 重载为友元函数
    (待补充)

  4. this指针
    this是一个无需特殊定义的指针,它隐含于每一个类的成员函数中,指向正在被某个成员函数操作的对象。
    如果某个对象调用的成员函数,则编译程序首先将这个对象的地址赋给this指针,然后调用成员函数。

4.3 虚函数

虚函数是一种非静态的成员函数,反映了基类和派生类之间的特殊关系,定义形式:

virtual 类型说明符 函数名 (参数表)

如果在基类中,某个函数被声明为虚函数,则在派生类中,该成员函数可能有其他的实现方法。
例子:

class Animal
{
public:
    void character()
    {
        cout<<"动物特征:不同"<<endl; 
    }
    virtual void food()
    {
        cout<<"动物食物:不同"<<endl;
    }
};

class Giraffe:public Animal
{
public:
    void character()
    {
        cout<<"长颈鹿:长脖子"<<endl;
    }
    virtual void food()
    {
        cout<<"食物:树叶"<<endl;
    }
};

class Elephent:public Animal
{
public:
    void character()
    {
        cout<<"大象:长鼻子"<<endl; 
    }
    virtual void food()
    {
        cout<<"食物:草"<<endl;
    }
};

void f(Animal &p)
{
p.character();
p.food();
}

void fg(Elephent &p)
{
p.character();
p.food();
}

int  main()
{
Giraffe g;
f(g);
Elephent e;
fg(e);
return 0;
}

运行结果:


image.png

在例程中,函数f的参数为基类指针,主函数调用时传入的为派生类的地址,此时,实际操作的是基类的成员函数,而对于虚函数,则会根据派生类对象的地址执行派生类中的函数。
虚函数的使用说明:
--派生类中的虚函数应与基类中的虚函数有相同的函数名、参数类型及个数;
--派生类的虚函数可以不显示声明;
--只有虚函数操作的是对象的指针或对象的引用时,对该虚函数的调用采取的才是动态联编。

4.4 抽象类

含有纯虚函数的类称为抽象类。
纯虚函数是指在基类中定义为空的虚函数。
抽象类的性质:
--只能用作其他类的基类,不能建立其对象;
--不能用作参数类型和函数返回值类型;
--可以说明指向抽象类指针引用,此指针可以指向其派生类。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容