zoukankan      html  css  js  c++  java
  • 面向对象的程序设计

     面向对象的程序设计

        面向对象(Object Oriented, OO)是当代计算软件开发方法;

        对象:现实世界中的各种具体的或抽象的“事物”。

    一 、类

        类是具有相同属性和行为的一组对象的集合,它为属于该类的全部对象提供了统一的抽象描述,其内部包括属性和行为两个主要部分

        1、语法格式:

            class 类名称

            {

              public://访问权限修饰符

                公有成员(外部可以调用)

              protected://访问权限修饰符

                保护成员(外部不可调用、派生类可以调用)

              private://访问权限修饰符

                私有成员(外部不可调用、派生类不可调用)

            };

        2、类对象

            类的对象是该类的某一特定实体,即类类型的变量。

            声明形式: 类名 对象名;

          3、类成员

            成员变量

              class 类名

              {

              访问权限:

                   类型  变量名;  //声明的时候不占用内存

              };

            注意:在定义成员变量时,不对成员变量赋初始值。

          成员函数

          可以写在类体重也可以写在类体外;

          示例:

    #pragma once
    #include <iostream>
    
    class Color
    {
    public:
        void setName(const char* n)
        {
            strcpy_s(name, n);
        }
    private:
        char name[32];
    }
    成员函数写在类体中
     1 Color.h
     2 #pragma once
     3 #include <iostream>
     4 
     5 class Color
     6 {
     7 public:
     8     void setName(const char* n);
     9 private:
    10     char name[32];
    11 };
    12 
    13 Color.cpp
    14 #include "stdafx.h"
    15 #include "Color.h"
    16 
    17 
    18 void Color::setName(const char* n)
    19 {
    20     strcpy_s(name, n);
    21 }    
    成员函数写在类体外

     

        4、类成员的访问

            (1)类中成员可以直接访问

                直接使用成员名。

            (2)类外访问

                对象使用“对象名.成员名”的方式访问public属性的成员。

                 指针使用“指针变量->成员名”的方式访问public属性的成员。

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    
    class Car//定义car类
    {
    public:
        void start()//成员函数
        {
            cout << color << "" << name << "启动了" << endl;
    
        }
        void stop()//成员函数
        {
            cout << color << "" << name << "停下了" << endl;
    
        }
        void set(const char * n, const char * c)//成员函数
        {
            strcpy_s(name, n);
            strcpy_s(color, c);
        }
    private:
        char name[32];//成员变量
        char color[32];//成员变量
    
    };
    
    int main()
    {
        Car a;//定义一个car类的对象a
        a.set("宝马", "红色");
        a.start();
        a.stop();
    
        Car b;
        b.set("奔驰", "蓝色");
        Car *p = &b;
        p->start();
        p->stop();
        return 0;
    }
    示例

    二、构造函数

      概念:构造函数是实现数据成员初始化的特殊成员函数

      特点:(1)与类同名,没有返回值;

         (2)创建对象时,构造函数被自动调用。每创建一个对象都必须调用一次构造函数,每调用一次构造函数必定创建一个对象。

       分类 : (1)无参构造函数:没有参数;

          (2)普通构造函数:普通参数;

          (3) 拷贝构造函数:参数为对象的引用。

      语法格式:

          (1)类中定义格式

              类名(形参列表)

              {…} //函数体,对数据成员赋值 类中声明,

          (2)类外定义

              类中声明 类名(形参列表);

              类外定义 类名::类名(形参列表)

                  {…} //函数体

      一、缺省的构造函数

      1、缺省构造函数的种类

        (1)系统自动产生的构造函数 类名() {}
          Α、 用户未定义构造函数时,系统会自动产生构造函数,用户一旦定义,系统就不会再产生构造函数。

        (2) 用户定义的无参构造函数

    class String
    {
    public:
        String()  //无参构造函数
         {
             memset(str, 0, sizeof(str));
             cout << "用户定义无参构造函数" << endl;
         }

        (3)用户定义的所有参数都有缺省值的构造函数。

         

     1 class String
     2 {
     3 public:
     4     String(char *s = nullptr)  //用户定义的所有参数都有缺省值的构造函数
     5     {
     6         cout << "所有参数都有缺省值" << endl;
     7         if (s == nullptr)
     8         {
     9             memset(str, 0, sizeof(str));
    10         }
    11         else
    12         {
    13             strcpy_s(str, s);
    14         }
    15     }

        2、缺省的构造函数的调用

          类名   类对象

            定义类对象时 有两件事发生:Α 、创建了类对象 。  Β、调用构造函数。

      二、有参的构造函数

        1、有参构造函数的定义

    1 class String
    2 {
    3 public:
    4     String(char *  p)  //有参构造函数
    5      {
    6          memset(str, 0, sizeof(str));
    7          cout << "用户定义有参构造函数" << endl;
    8      }
    有参构造函数

        2、有参构造函数的调用

    int main()
    {
        String s("Hello world!");
        s.output();
        return 0;
    }

      三、拷贝构造函数

        概念:拷贝构造函数是一种特殊的构造函数,其形参为本类的对象的引用

          作用:从一个已有对象,来初始化新创建的对象

        语法格式:

                class 类名

                 {

                  public: 类名() {…} //无参构造函数

                  类名(形参列表) {…} //有参构造函数

                  类名(类名& 对象名) {…} //拷贝构造函数 };

                 // 调用拷贝构造函数的场景(假设有Car c1定义):

                  Car c2 = c1;

                  Car c3(c1);

    class String
    {
    public:
        String(const char* s)
        {
            //memset(str, 0, sizeof(str));
            strcpy_s(str, s);
        }
    
        //拷贝构造函数
        String(const String& other) //一般都这么写other
        {
            strcpy_s(str, other.str);
        }
    
        void output()
        {
            cout << str << endl;
        }
    private:
        char str[128];
    };
    
    int main()
    {
        String a("123453333333333333333333333333333333333");
        String b(a);
        cout << "a=";
        a.output();
    
        cout << "b=";
        b.output();
        return 0;
    }

      四、浅拷贝和深拷贝

        从已存在对象来创建新的对象,系统会调用拷贝构造函数,如果用户没有定义拷贝构造函数,则系统会自动生成默认的拷贝构造函数,进行值拷贝。如果用户定义了拷贝构造函数,系统会调用用户顶替拷贝构造函数,进行拷贝。

        (1)默认拷贝构造函数可以完成对象的数据成员简单的复制,实际上默认的拷贝构造函数只是将指针复制。

       (2)在对含有指针成员的对象进行拷贝时,必须要自己定义拷贝构造函数,使拷贝后的对象指针成员有自己的内存空间,即进行深拷贝,这样就避免了内存泄漏发生。

      浅拷贝:类成员变量中有指针,文件、句柄等情况下,浅拷贝就有可能让类无法正常工作。

        原因:以指针为例,拷贝只是复制了指针变量的地址,而其内存数据未拷贝,一旦s1释放内存,等到s2释放内存时,已找不到指针所指向的内存,此时为野指针,带来的后果具有不可预测性,程序执行到此会崩溃,

                             

      深拷贝:让新产生的对象,其成员变量对资源的引用操持独立,相互之间不受影响,仅保持“值”相同。

                     

      深拷贝示例

    #include "stdafx.h"
    #include <iostream>  
    using namespace std;
    
    class Student
    {
    private:
        int num;
        char *name;
    public:
        Student();
        ~Student();
        Student(const Student &s);//拷贝构造函数,const防止对象被改变
    };
    
    Student::Student()
    {
        name = new char(20);
        cout << "Student" << endl;
    
    }
    Student::~Student()
    {
        cout << "~Student " << (int)name << endl;
        delete name;
        name = NULL;
    }
    Student::Student(const Student &s)
    {
        name = new char(20);
        memcpy(name, s.name, strlen(s.name));
        cout << "copy Student" << endl;
    }
    
    int main()
    {
        {
            Student s1;
            Student s2(s1);// 复制对象
        }
    
        return 0;
    }
    深拷贝

       总结: 浅拷贝会把指针变量的地址复制; 深拷贝会重新开辟内存空间

    三、析构函数

      1、概念:用于撤销对象的成员函数造成的垃圾,比如开辟的空间(对象释放前系统自动调用)。、 

      2、语法格式

          (1)类中定义

              ~类名()

              {…} //函数体

          (2) 类中说明,类外定义

             Α  类中声明

              ~类名(); //类中声明

               Β   类外定义

              类名::~类名()

              {…} //函数体

      3、注意事项:

        (1)析构函数的名称由运算符“~”与类名组成;

        (2)析构函数无参数,无返回值;

        (3)析构函数不可以重载,即一个类只有一个析构函数;

        (4)如果用户没有定义,系统会自动生成默认析构函数: 类名::~类名() {}

        (5)当类中用new运算符分配了动态空间时,必须定义析构函数,并在函数体中用delete运算符释放动态存储空间。

        (6)析构函数调用与构造函数相反,先构造的后析构,后构造的先析构;

      4、示例

    #include "stdafx.h"
    #include <iostream>
    #include <string.h>
    using namespace std;
    class String
    {
    public:
        String(const char *str)
        {
            int len = strlen(str) + 1;
            p = new char[len];
            strcpy_s(p, len, str);
            cout << "创建对象" << endl;
        }
    
        void output()
        {
            cout << p << endl;
            cout << "打印对象" << endl;
        }
    
        ~String()
        {
            if (p != nullptr)
            {
                delete [ ] p;
                p = nullptr;
                cout << "对象析构" << endl;
            }
        }
    
    private:
        char *p;
    };
    
    int main()
    {
        String s("hello world");
        s.output();
        return 0;
    析构函数字符串练习

    四、成员变量初始化

      1、类成员变量初始有两种方式

        方式一: 在构造函数体中对成员变量进行赋值

        语法: 类名(形参列表)

        {

          成员变量1 = 表达式;

          成员变量2 = 表达式;

          …

          成员变量n = 表达式;

        }

        方式二:使用初始化列表

          语法: 类名(形参列表) : 对象名1(实参列表) , 对象名1(实参列表) , … ,对象名n(实参列表)

          {

            … //函数体

          }

      2、成员变量初始化的顺序

        (1)成员变量的初始化顺序和成员变量声明顺序相关,先声明,先初始化

          

        (2)先调用对象成员所属类的构造函数,再执行自身类的函数体

         

    五、成员变量和成员变量函数

    1、静态成员

      分类:静态成员变量和静态成员函数

       静态成员变量

        1、格式语法

              class 类名 {

                     访问权限:

                        static 类型 变量名;

                     };

        2、说明:

            (1)静态成员变量不属于某个对象,而是属于类,它由某个类的所有对象共有。

            (2)静态成员变量的定义 静态成员变量定义后,必须对它进行初始化,并且一定要在类外进行。

            (3)静态成员变量只能够初始化一次

            (4)初始化的形式如下: 类型 类名::静态成员变量 = 表达式; 初始化时不加关键字static

        3、示例:

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    
    class Point
    {
        int x;
        int y;
    
    public:
        static int no;//声明静态成员变量
        Point(int a, int b) :x(a), y(b)
        {
            ++no;
        }
    };
    
    int  Point::no = 0;//必须在类体外初始化静态成员变量
    
    void  main()
    {
        Point p(1,10);
        Point p1(2,4);
        cout << Point::no << endl;//调用静态成员变量
    }
    静态成员变量

      静态成员函数

        语法格式:

             class 类名 {

               访问权限:

                  static 返回值 函数名(形参列表);

                  }

        说明:

        (1)静态成员函数也从属于类,由同一个类的所有对象共同拥有。

        (2)静态成员函数只能直接访问该类的静态成员变量、静态成员函数以及类以外的数据和函数,而访问非静态成员必须通过参数传递方式得到类的对象,然后通过对象名来访问。

        (3)在类内可以直接通过函数名称访问。

        (4)在类外可以通过 类名::静态成员函数(实参)、对象.静态成员函数(实参)、指针->静态成员函数(实参) 来访问。

    class Point
    {
        int x, y;
        static int no;    //声明静态成员变量
    public:
        Point(int a, int b) : x(a), y(b) { ++no; }
        static void print() {     //静态成员函数
            std::cout << “no=” << no << std::endl;
        }
    };
    int Point::no = 0; 
    void main(){
        Point pt(1, 10);
        Point* p1 = new Point(3, 3);
        pt.print();       //对象名.静态成员函数名()
        p1->print();    //指针->静态成员函数名()
        Point::print(); //类名::静态成员函数名()
    }
    静态成员函数

    六、函数的重载

      概念:函数重载是指在同一作用域内,可以有一组相同函数名不同参数列表的函数,这组函数被称为重载函数。

      作用:达到行为标识符统一

      判断依据函数名称、形参列表(参数类型或个数)和返回值无关

      示例

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    
    int  add(int a, int b)
    {
        return a + b;
    }
    
    double  add(double a, double b)
    {
        return a + b;
    }
    
    const char*  add(char *a,char *b)
    {
        strcat_s(a,100,b);
        return a;
    }
    
    int main()
    {
        char s[100] = {"hello "};
        cout << add(10, 10) << endl;
        cout << add(10.9, 10.5) << endl;
        cout << add(s, "world") << endl;
        return 0;
    函数重载

      常成员函数:(1)在一个类中使用const关键字说明的成员函数,称为常成员函数

            (2)常成员函数不能修改成员变量的值,也不能调用该类中其它没有用const修饰的成员函数。

                                

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    
    class S
    {
        int a;
    public:
        S(int x) :a(x)
        {
        }
    
        void print()
        {
            cout << a << endl;
        }
    
        void print()const;
    };
    
    void S::print()const
    {
        cout <<"const"<< a << endl;
    }
    
    int main()
    {
        S a(10);
        a.print();//调用非常成员函数
        const S& a1 = a;
        a1.print();//调用常成员函数
        return 0;
    }
    View Code

    运算符重载

      概念运算符重载就是赋予已有运算符多重含义

      语法格式:

          class 类名

          {

            public:

                类型 operator 运算符(形参列表)

                {  函数体 }

          };

      原则:

        (1)被重载的运算符不改变原来的操作数个数、优先级和结合性。

        (2)不能创新发明。

        (3)不能改变运算符对预定义类型(基本类型)的操作方式。

        (4)成员函数实现运算符重载时,运算符的左操作数为当前对象。

       (5)如果重载的运算符函数是双目的运算符,则参数表中有一个参数,若为单目运算符,则参数表中有零个参数(没有参数),特殊情况除外,比如++、--运算符。

      不可重载的运算符:

        

       实现方式:成员函数  和  友员函数

  • 相关阅读:
    GNOME3启动时出错:Oh no! Something has gone wrong.Logout!
    设计模式之迭代器模式
    C#中多线程 委托的使用
    python的memcache使用如果对key设置了一个int型
    Linux select TCP并发服务器与客户端编程
    G++ 教程(转)
    汉语-词语:卓识
    汉语-词语:决断
    汉语-词语:远见
    汉语-词语:胆识
  • 原文地址:https://www.cnblogs.com/hyt19911004/p/12535924.html
Copyright © 2011-2022 走看看