zoukankan      html  css  js  c++  java
  • C++基础之面向对象

    一、结构化程序设计与面向对象程序设计

    1、结构化程序设计

    • 在结构化程序设计中,采用自顶向下、逐步求精及模块化的思想,将复杂的大问题层层分解为许多简单的小问题。
    • 在编写程序时,使用3种基本控制结构来构造程序。可以说,程序基本上都含有顺序、选择、循环3种基本控制结构,这3种结构到目前为止仍是主要的控制结构。程序以控制结构为单位,只有一个入口和一个出口,基于控制结构可以从前往后地顺序阅读程序,程序的静态描述与执行时的控制流程容易对应,所以可以独立地理解各个部分。结构化程序设计主要强调的是程序的易读性

    2、面向对象程序设计的概念和特点

    • 所谓面向对象的程序设计方法,就是使分析、设计和实现一个系统的方法尽可能地接近人们认识一个系统的方法。通常包括3个方面:面向对象的分析、面向对象的设计和面向对象的程序设计
    • 面向对象技术把问题看成是相互作用的事物的集合,也就是对象的集合。对象具有两个特性:一是状态;状态是指对象本身的信息,也称为属性二是行为,行为是对对象的操作。通过对事物的抽象找出同一类对象的共同属性(静态特征)和行为(动态特征),从而得到类的概念。对象是类的一个具象,类是对象的一个抽象

    3、面向对象的程序设计有“抽象”“封装”“继承”和“多态”4个基本特点。

    • 抽象:对象是系统中用来描述客观事物的一个实体,如各位员工是员工类的一个个对象。对象的特点包括两个方面:属性和操作。属性指的是描述对象静态特征的数据项,如员工的姓名、职位、薪水等,可以用变量来表示;操作指的是描述对象动态特征(即行为)的函数序列,也称为方法或服务,如员工可以请假、加班,员工还可以获得提拔、加薪等。C++中使用对象名、属性和操作三要素来描述对象
    • 封装:在C++中,通过用户定义的类来支持数据封装和信息隐藏
    • 继承:在C++现有类的基础上可以声明新的类,将一个已有类中的数据和函数保留,并加上自己特殊的数据和函数,从而构成一个新类,这就是继承和复用的思想。原来的类是基类,也称为父类或超类。新类是派生类,也称为子类
    • 多态:多态是指不同种类的对象都具有名称相同的行为,而具体行为的实现方式却有所不同。在一个类或多个类中,可以让多个方法使用同一个名字,从而具有多态性。这是通过函数重载及运算符重载实现的多态

    二、类的定义

    1. 字母、数字和下划线的组合,大小写敏感,但不能以数字开头,也不能和系统中使用的关键字完全相同。
    2. 类是具有唯一标识符的实体,就是说类名不能重复。类定义以“;”结束,大括号中的部分称为类体。
    3. 定义类时系统并不为类分配存储空间,而只是把类看作是一种模板或样板。或者说,类可以看作是用户自定义的一种数据类型。在C++98标准下,类中声明的任何成员不能使用auto、extern和register关键字进行修饰。
    4. 类中的成员按功能划分,包括成员变量和成员函数;按访问权限划分,包括公有成员成、私有成员和保护员。
    5. 在C++中还可以定义不是任何类的成员的函数,这样的函数可称为“全局函数”
    6. 成员函数既可以在类体内定义,也可以在类体外定义。如果成员函数定义在类体内部,则默认是内联函数。也可以在类体内部声明函数,并加上inline关键字,然后在类体外给出函数定义,这样的成员函数也是内联函数。
    7. 如果成员函数定义在类体外,则类体内必须要有函数原型,类体外函数定义的前面必须用“类名::”来限定,格式如下:
    8. 返回值类型 类名::成员函数名(参数列表){
        成员函数的函数体
      }
    9. 类名是成员函数所属类的名字,符号::是类作用域运算符,表明它后面的成员函数是属于类名标识的这个类的。返回值类型就是这个成员函数返回值的类型。
    10. 类C中不能定义类C的成员变量,但可以定义类C的指针和引用。
    11. 成员函数并非每个对象各自存有一份。成员函数和普通函数一样,在内存中只有一份,它可以作用于不同的对象,为类中各对象共享。
    12. 通常,因为函数体代码较长,所以在类体内仅给出成员函数的原型,然后在类体外给出对应的函数体。如果函数体定义在类体内,则系统将其视为内联函数。类中定义的成员函数允许重载。

     

     三、C++程序结构

    一个完整的C++程序包括以下几部分:

    • ✓ —个主函数,可以调用其他函数,但不能被调用,也称为主程序。
    • ✓ 用户定义的任意多个的类及全局函数。
    • ✓ 全局说明。在所有函数和类定义之外的变量说明及函数原型。
    • ✓ 注释。
    • ✓ 头文件。
    • 对于比较大的程序,根据主函数和各用户定义的类及全局函数的功能及相互关系,可以把类及全局函数划分为几个程序文件,包括.cpp文件和.h文件。.cpp文件是源程序文件,.h文件是头文件。
    • 从逻辑关系上看,典型的C++程序的结构包括类的定义、类中成员函数的实现及主函数main。

    四、Class 中的访问权限

    • public(公有的): 使用它修饰的类的成员可以在程序的任何地方被访问。
    • private(私有的): 使用它修饰的类的成员仅能在本类内被访问。
    • protected(保护的): 它的作用介于public与private之间,使用它修饰的类的成员能在本类内及子类中被访问。
    • 私有类型的成员在类外是不能访问的,通过公有函数访问的效率比直接访问的效率要低。为了权衡这两方面的因素,C++提供了友元访问方式。只有在类内和在友元函数内才可以访问私有成员。

    五、Class 的使用

    1、构造函数

    • 为了对对象进行初始化,C++提供了一种称为构造函数的机制,用于对对象进行初始化,实际上是用来为成员变量赋初值的。
    • 构造函数是类中的特殊成员函数,它属于类的一部分。给出类定义时,由程序员编写构造函数。如果程序员没有编写类的任何构造函数,则由系统自动添加一个不带参数的构造函数。
    • 声明对象后,可以使用new运算符为对象进行初始化,此时调用的是对象所属类的构造函数。构造函数的作用是完成对象的初始化工作,用来保证对象的初始状态是确定的。在对象生成时,系统自动调用构造函数,用户在程序中不会直接调用构造函数。
    • 定义一个类时,需要为类定义相应的构造函数。构造函数的函数名与类名相同,没有返回值。一个类的构造函数可以有多个,即构造函数允许重载。同一个类的多个构造函数的参数表一定不能完全相同。
    • 当类中没有定义任何构造函数时,系统会自动添加一个参数表为空、函数体也为空的构造函数,称为默认构造函数。所以任何类都可以保证至少有一个构造函数。如果程序员在程序中已经定义了构造函数,则系统不会再添加默认构造函数。
    • 在声明类的构造函数时可以同时给出函数体,这样的构造函数称为内联构造函数。也可以在类体外给出构造函数的定义。构造函数的声明中,形参的个数可以为0,即参数表为空

    构造函数的格式:

    构造函数的声明格式如下:类名(形参1, 形参2, …,形参n)
    在类体外定义构造函数时通常有三种形式
    //形式一:
    类名::类名(形参1,形参2,…,形参n):x1(形参1), x2(形参2), …, xn(形参n){}
    //形式二:
    类名::类名(形参1,形参2,…,形参n){
        x1=形参1;
        x2=形参2;
        ……
        xn=形参n;
    }
    //形式三:
    类名::类名(){
        x1=初始化表达式1;
        x2=初始化表达式2;
        ……
        xn=初始化表达式n;
    }

    初始化时机:定义类的成员函数、成员对象及友元函数时,均不调用类的构造函数。仅当定义类的对象时,才由系统自动调用类的构造函数

    构造函数的使用:

    • C++语言规定,创建类的任何对象时都一定会调用构造函数进行初始化。对象需要占据内存空间,生成对象时,为对象分配的这段内存空间的初始化由构造函数完成。
    • 数组:特别地,如果程序中声明了对象数组,即数组的每个元素都是一个对象,则一定要为对象所属的这个类定义一个无参的构造函数。因为数组中每个元素都需要调用无参的构造函数进行初始化,所以必须要有一个不带参数的构造函数。
    class Person{
    private:
        int id;
        string name;
    public:
        Person();
        Person(int id,string name);
        ~Person();
        void setName(string name);
        string getName();
        void setId(int id);
        int getId();
        static void print(Person *person){
            cout<< "id:" << person->id << " name:" << person->name <<endl;
            delete person;
        }
    };
    Person::Person(){
        cout<<"无惨构造函数" <<endl;    
    }
    Person::Person(int id,string name){
        this->id=id;
        this->name=name;
        cout << "有2个参数的构造函数" <<endl;
    }
    int main(){
        Person pe();
        Person pe2=Person(1,"张三");
    }

    1、使用new关键字

    用new创建对象时返回的是一个对象指针,这个指针指向本类刚创建的这个对象。C++分配给指针的仅仅是存储指针值的空间,而对象所占用的空间分配在堆上。使用new创建的对象,必须用delete来撤销。

        //4、类名 *对象指针名 = new 类名;
        Student *stu4=new Student(3,'D');
        cout<< stu4->getName() <<endl;

    类名 &对象引用名 = 对象;

        Student stu3=Student(3,'C');
        Student &str4=stu3;
        cout << str4.getName()<<endl;

    类名 *对象指针名 = 对象的地址;

        Student stu3=Student(3,'C');
        Student *str4=&stu3;
        cout << str4->getName()<<endl;

    类名 对象数组名[数组大小];

        Student stu3[10];
        Student stu4=Student(3,'C');
        stu3[0]=stu4;

    同类型的对象之间可以相互赋值。对象和对象指针都可以用作函数参数。函数的返回值可以是对象或指向对象的指针。

    2、 复制构造函数(拷贝构造函数):

    复制构造函数是构造函数的一种,也称为拷贝构造函数。它的作用是使用一个已存在的对象去初始化另一个正在创建的对象。例如,类对象间的赋值是由复制构造函数实现的。
    复制构造函数只有一个参数,参数类型是本类的引用。复制构造函数的参数可以是const引用,也可以是非const引用。一个类中可以写两个复制构造函数,一个函数的参数是const引用,另一个函数的参数是非const引用。这样,当调用复制构造函数时,既能以常量对象(初始化后值不能改变的对象)作为参数,也能以非常量对象作为参数去初始化其他对象。
    复制构造函数的原型如下:

    • 格式一 A::A(const A&)
    • 格式二 A::A(A &)

    自动调用复制构造函数的情况有以下3种:
    1)当用一个对象去初始化本类的另一个对象时,会调用复制构造函数。例如,使用下列形式的说明语句时,即会调用复制构造函数。

    • 类名 对象名2(对象名1);
    • 类名 对象名2=对象名1;

    2)如果函数F的参数是类A的对象,那么当调用F时,会调用类A的复制构造函数。换句话说,作为形参的对象,是用复制构造函数初始化的,而且调用复制构造函数时的参数,就是调用函数时所给的实参。
    3)如果函数的返回值是类A的对象,那么当函数返回时,会调用类A的复制构造函数。也就是说,作为函数返回值的对象是用复制构造函数初始化的,而调用复制构造函数时的实参,就是retrun语句所返回的对象。
    注意,在复制构造函数的参数表中,加上const是更好的做法。这样复制构造函数才能接收常量对象作为参数,即才能以常量对象作为参数去初始化别的对象。

    class Person{
        public:
            Person(const Person &person);
            Person(Person &person);
    }
    Person::Person(const Person &person){
       cout << "const类型 copy 构造函数" << endl;
    }
    Person::Person(Person &person){
         this->id=person.id;
         this->name=person.name;
         cout << "copy 构造函数" << endl;
    }

     3、析构函数:

    • 与构造函数一样,析构函数也是成员函数的一种,它的名字也与类名相同,但要在类名前面加一个“~”字符,以区别于构造函数。析构函数没有参数,也没有返回值。一个类中有且仅有一个析构函数如果程序中没有定义析构函数,则编译器自动生成默认的析构函数。析构函数不可以多于一个,不会有重载的析构函数。默认析构函数的函数体为空。
    • 创建对象时自动调用构造函数,那么,什么时候调用析构函数呢?可想而知,在对象消亡时自动调用析构函数。析构函数的作用是做一些善后处理的工作。例如,如果在创建对象时使用new运算符动态分配了内存空间,则在析构函数中应该使用delete释放掉这部分占用的空间,保证空间可再利用。
    • 当使用new运算符生成对象指针时,自动调用本类的构造函数。使用delete删除这个对象时,首先为这个动态对象调用本类的析构函数,然后再释放这个动态对象占用的内存
    • 如果是创建普通对象:Person p();则无需使用delete p释放否空间,他在程序结束的时候会自动调用析构函数
    class Person{
        public:
            ~Person();
    }
    Person::~Person(){
         cout<< "析构函数" << endl;
    }

    对于对象数组,要为它的每个元素调用一次构造函数和析构函数。全局对象数组的析构函数在程序结束之前被调用。

  • 相关阅读:
    5G网络逐渐普及TSINGSEE青犀视频云边端架构网页视频实时互动直播系统又将如何发展?
    【开发记录】TSINGSEE青犀视频云边端架构Visual Studio 2017自建WebRTC中peerconnection_client编译报无法解析错误
    安防视频云服务平台EasyCVR视频智能分析系统运行控制台报404错误如何排查?
    一对一或一对多音视频通话会议系统可以通过哪些方式实现?
    TSINGSEE青犀视频云边端视频智能分析平台开发VMware下安装Ubuntu系统后无法安装VMwaretools问题解决
    最简单的Windows套接字(Socket)例子(源码,实例)
    KJAVA虚拟机Hack笔记实现MIDP的SLAVE事件模型
    系统程序员成长计划你的数据放在哪里(下)
    使用new实现realloc操作
    KJava虚拟机hack笔记基于GTK的移植
  • 原文地址:https://www.cnblogs.com/jalja365/p/12956220.html
Copyright © 2011-2022 走看看