C++面向对象基础篇

C++是一门面向对象的语言,本篇就C++面向对象中类与对象的特性做相关整理

类与对象

类的对象和概念

变量 + 函数 进行捆绑封装形成一个类

  • 定义类

    class Employee
    {
    private:
        char sname[20];
    
    public:
        int salary;
    
        void setName(const char *name);
        void getName(char *name);
        void avrageSalary(Employee e1, Employee e2);
    };	// 必须要有分号
    
    // 成员变量的函数可在类中进行实现,同样也可在外部实现
    // 外部实现方式 类名 :: 方法
    void Employee ::setName(const char * name)
    {
        strcpy(sname, name);
    }
    
    void Employee ::getName(char *name)
    {
        strcpy(name, sname);
    }
    
  • 使用类

    Employee emp;
    // 对象名.成员名
    emp.setName("Rick");
    emp.salary = 10000;
    // 指针->成员名
    Employee *p = &emp;
    p->salary = 5000;
    // 引用名.成员名
    CEmployee &r = emp;
    r.salary = 8000;
    

访问权限

private 私有成员,只能在成员函数中访问
public 公有成员,可以在任何地方访问
protect 保护成员,暂不介绍

默认函数

  • 构造函数

    class Line{
       public:
          Line();  		// 这是默认空构造函数
      		Line(double length);	// 带参构造函数
       private:
          double length;
    }; 
    int main( ){
       Line line1;
    	 Line line2(5.0)
    }
    
  • 析构函数

    类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。

    析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源

    #include <iostream>
     
    using namespace std;
     
    class Line
    {
       public:
          void setLength( double len );
          double getLength( void );
          Line();   // 这是构造函数声明
          ~Line();  // 这是析构函数声明
     
       private:
          double length;
    };
     
    // 成员函数定义,包括构造函数
    Line::Line(void)
    {
        cout << "Object is being created" << endl;
    }
    Line::~Line(void)
    {
        cout << "Object is being deleted" << endl;
    }
     
    void Line::setLength( double len )
    {
        length = len;
    }
     
    double Line::getLength( void )
    {
        return length;
    }
    // 程序的主函数
    int main( )
    {
       Line line;
       // 设置长度
       line.setLength(6.0); 
       cout << "Length of line : " << line.getLength() <<endl;
       return 0;
    }
    
    Object is being created
    Length of line : 6
    Object is being deleted
    

this指针

并非对象的成员,是常量指针

每个对象可以使用this指针访问自己的地址

非static成员函数调用时,this指针为隐式参数

用途:防止自赋值、返回以连续调用

class Rick
{
private:
    int age;
public:
    Rick();
    Rick(int age);
  	Rick* ReturnAddress() {
      return this;
    }
};

Rick::Rick(int age)
{
    this->age = age;
}

函数模板

例如实际问题中的需要:对于不同类型数据可以用的排序函数sort

模板中class也可写成 typename

template <class T>
return-type sort(...T...)

template <class T1, class T2>
template <class T>
void print(const T array[], int size){
    int i;
    for (i = 0; i < size; i++) {
        cout << array[i];
    }
    return;
}
#include <iostream>
using namespace std;

template <class T>
T Max(T a, T b) {
    cout << "TemplateMax" << endl;
    return 0;
}

template <class T1, class T2>
T1 Max(T1 a, T2 b) {
    cout << "TemplateMax2" << endl;
    return 0;
}

double Max(double a, double b) {
    cout << "MyMax" << endl;
    return 0;
}

int main() {
    int i = 4, j = 5;
    Max(1.2, 3.4);      // MyMax
    Max(i, j);          // TemplateMax
    Max(1.2, 3);        // TemplateMax2
    return 0;
}

在有多个函数和函数模板名字相同的情况下,编译器如下处理:

  • 先找参数完全匹配的普通函数(非由模板实例化而得的函数)
  • 再找参数完全匹配的模板函数
  • 再找是参数经过自动类型转换后能够匹配的普通函数
  • 上面的都找不到,则报错

类模板

为了多快好省地定义出一批相似的类,可以定义类模板,然后由类模板生成不同的类

类模板:在定义类的时候给它一个/多个参数,这个/些参数表示不同的数据类型。在调用类模板时,指定参数,由编译系统根据参数提供的数据类型自动产生相应的模板类

#include <iostream>
using namespace std;

template <class T>  // 类模板首部,声明类模板参数
class Carray
{
    T *ptrElement;
    int size;
public:
    Carray(int length);
    ~Carray();
    int len();
    void setElement(T arg, int index);
    T getElement(int index);
};
int main(int argc, char const *argv[])
{
  	// 创建一个元素类型为int的Carray模板类,而声明该模板类的一个对象、以及一个指针
    Carray <int> arrayInt(50), *ptrArrayInt;
    return 0;
}

函数模板作为类模板成员

#include <iostream>
using namespace std;

template <class T>
class A {
    public:
        // 成员模板函数
        template <class T2>
        void Func(T2 t) {
            cout << t;
        }  
};

int main() {
    A<int> a;
    a.Func('K');    // K
    a.Func("hello");    // hello
    return 0;
}

类模板的“<类型参数表>”中可以出现非类型参数

template <class T, int size>
class Array {
    T array[size];
    public:
        void Print() {
            for (int i = 0; i < size; i++) {
                cout << array[i] << endl;
            }
        }
};

int main() {
    Array<double, 40> a1;
    Array<int, 50> a2;
    return 0;
}

static

  • static修饰成员变量就是静态成员变量,静态成员变量一共就一份,为所有对象共享,静态成员不需要通过对象就能访问到

  • sizeof运算符不会计算静态成员变量

    class MyClass {
        int n;
        static int n;
    };
    sizeof(MyClass)   = 4
    
  • static修饰成员函数就是静态成员函数,普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象

    #include <iostream>
    using namespace std;
    
    class Rectangle {
        private:
            int w,h;
            static int nTotalArea;
            static int nTotalNumber;
        public:
            Rectangle(int w_, int h_);
            ~Rectangle();
            static void PrintTotal();
    };
    // 必须在定义类的文件中对静态成员变量进行一次说明或初始化,否则链接不能通过
    int Rectangle::nTotalNumber = 0;
    int Rectangle::nTotalArea = 0;
    
    Rectangle::Rectangle(int w_, int h_) {
        w = w_;
        h = h_;
        nTotalNumber ++;
        nTotalArea += w * h;
    }
    
    Rectangle::~Rectangle() {
        nTotalNumber--;
        nTotalArea -= w * h;
    }
    /**
     * 在静态成员函数中,不能访问费静态成员变量,也不能调用非静态成员函数
     */
    void Rectangle::PrintTotal() {
        cout << nTotalNumber << "," << nTotalArea << endl;
    }
    
    int main(void){
        Rectangle r1(3,3), r2(2,2);
        Rectangle::PrintTotal();
        r1.PrintTotal();
        return 0;
    }
    

成员变量和封闭类

有成员对象的类叫 封闭类

#include <iostream>
using namespace std;

class CTyre {
    private:
        int radius;
        int width;
    public:
        // 构造器初始化列表
        CTyre(int r, int w) : radius(r), width(w) {}
};

class CEngine{

};

class CCar {
    private:
        int price;
        CTyre tyre;
        CEngine engine;
    public:
        CCar(int p, int tr, int tw);  
};

CCar::CCar(int p, int tr, int tw) : price(p), tyre(tr, tw) {}

int main(void) {
    CCar car(20000, 17, 225);
    return 0;
}

这个例子中如果CCar不定义构造函数, CCar car 会报错,因为编译器不明白 car.tyre该如何初始化。car.engine的初始化没问题,用默认构造函数即可。

任何生成封闭类对象的语句,都要让编译器明白,对象中的成员对象是如何初始化的。

具体做法就是:通过封闭类的构造函数的初始化列表

常量对象、常量成员函数

  • 常量对象:如果不希望某个对象的值被改变,则定义该对象的时候可以在前面加const关键字

    class Demo {
      private:
      	int value;
      public:
      	void setValue() {}
    };
    const Demo Obj;		// 常量对象
    
  • 常量成员函数:在函数说明后面加const关键字。常量成员函数执行期间不应修改其所作用的对象。因此,在常量成员函数中不能修改成员变量的值(静态成员变量除外)

    class Sample {
        public:
            int value;
            void GetValue() const;
            void func();
            Sample();
    };
    
    void Sample::GetValue() const {
        // value = 0      wrong!
        // func();      wrong!
    }
    
    int main(void) {
        const Sample s;
        s.value = 100;      // Error  常量对象不可修改
        s.func();           // Error  常量对象上面不能执行非常量成员函数
        s.GetValue();       // OK     常量对象上可以执行常量成员函数
        return 0;
    }
    
  • 常引用:引用前面加const关键字,称为常引用。不能通过其常引用,修改其引用的变量

    class Sample {
      ...
    }
    // 当我们去传递引用参数的时候,不能避免o无意被修改,这时就需要常引用
    void PrintObj(Sample &o) {
      ...
    }
    // 这样就能确保不会出现无意中更改o值得语句
    void PrintObj(const Sample &o) {
      ...
    }
    

友元

友元分为友元函数和友元类两种

  • 类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数

    #include <iostream>
    using namespace std;
    
    class Car;  //提前声明Car类
    class Driver {
        public:
            void ModifyCar(Car* pCar);
    };
    
    class Car {
        private:
            int price;
        friend int MostExpensiveCar(Car cars[], int total); // 友元声明
        // 友元声明:可以将一个类的成员函数(包括构造、析构函数)说明为另一个类的友元
      	friend void Driver::ModifyCar(Car* pCar);   
    };
    
    void Driver::ModifyCar(Car* pCar) {
        pCar->price += 1000;
    }
    
    // 不是任何类的成员函数
    int MostExpensiveCar (Car cars[], int total) {
        int tmpMax = -1;
        for (int i = 0; i < total; i++) {
            if (cars[i].price >tmpMax) {
                tmpMax = cars[i].price;
            }
        }
        return tmpMax;
    }
    
  • 友元类:如果A是B的友元类,那么A的成员函数可以访问B的私有成员。(友元类之间的关系不能传递,不能继承

    #include <iostream>
    using namespace std;
    
    class Car {
        private:
            int price;
        friend class Driver;		// 声明Driver为友元类
    };
    
    class Driver {
        public:
            Car myCar;
            void ModifyCar() {
                myCar.price += 1000;
            }
    };
    
赞赏