类和对象是C++的重要特性,也是其面向对象的基本,类的类容很多,本文只是简单介绍,包括类的定义、构造函数、析构函数、常量函数、仿函数、继承、多态、静态成员和类模板等

1. 类的定义

类是自定义的类型,基本形式如下所示,类的末尾有分号";"

class Master
{
public:
    Master();     //构造函数
    ~Master();    //析构函数

//成员函数
public:
    void addBloodVolume();
    void reduceBloodVolume();
    void showBloodVolume();
    
    //在类的内部实现,默认是inline
    void setBloodVolume(int32_t bloodVolume)
    {
        m_blood_volume = bloodVolume;
    }

    void addManaVolume();
    void reduceManaVolume();
    void showManaVolume();

    //在类的内部实现,默认是inline
    void setManaVolume(int32_t manaVolume)
    {
        m_mana_volume = manaVolume;
    }

//成员变量
private:
    int32_t m_blood_volume;
    int32_t m_mana_volume;
};

2. 构造函数与析构函数

构造函数
构造函数决定了我们定义类对象的方式,默认是public,可以有多个

//第一种:
*.h文件
Master();

*.cc文件
Master::Master()
{

}

//第二种:
*.h文件
Master(){};

//第三种:
Master() = default;   //编译器会自动生成默认的函数定义体

构造函数支持重载

Master() = default;
Master(int32_t bloodVolume, int32_t manaVolume);

//类对象的定义:
Master master;
//或
Master master(100, 90);

构造函数的初始化列表

Master(int32_t bloodVolume, int32_t manaVolume) : m_blood_volume(bloodVolume), m_mana_volume(manaVolume){ /* code */};     //构造函数

初始化 const 成员变量的一种方法就是使用初始化列表

//m_blood_volume如果为const,则该成员变量支持初始化列表赋值
const int32_t m_blood_volume;

析构函数
析构函数只能有1个,不能重载,一个类只能有一个析构函数,对象在销毁的时候自动调用;如果用户没有定义,则系统会自动生成

~Master();

空类,编译器一般会给类生成默认的6个函数;如果不使用的话,可以将其定义为private,并且在后面加delete

public:    
    Master();     //默认构造函数
    ~Master();      //默认析构函数
private:
    Master(const Master &)=delete;    //默认拷贝构造函数
    Master(Master &&)=delete;    //默认移动构造函数
    Master &operator=(const Master &)=delete;   //默认赋值函数    
    Master &operator=(Master &&)=delete;    //默认移动赋值构造函数

3. public/private/protect

类的成员函数和成员变量有3种属性

class  Games
{
//公有
public:
    Games();
    ~Games();
//私有
private:
//保护
protected:

};
  • 公有(public):在类内以及通过类的实例都可以访问
  • 私有(private):在类内可以访问,类的实例不可以访问,子类不可以访问
  • 保护(protect):在类内可以访问,类的实例不可以访问,子类可以访问

一般情况下,将对外的接口定义为public,其余定位为private,尤其是成员变量;protect根据使用情况而定;

4. 虚函数与纯虚函数

虚函数的关键字virtual;如果一个函数声明为虚函数,则允许基类的指针指向子类的该函数,该函数需要在子类中实现

基类

//声明
virtual void skill_q();

//实现
void Master::skill_q()
{
    std::cout<<"skill q belong to master!"<<std::endl;
}

子类

class Karthus : public Master
{
public:
    Karthus() = default;
    ~Karthus(){};
public:
    void skill_q();
    void skill_w();
    void skill_e();
    void skill_r();
};

实现
void Karthus::skill_q()
{
    std::cout<<"skill q belong to karthus!"<<std::endl;    
}

使用

int main(int argc, char *argv[])
{
    Master *pmaster  = new Master; 
    pmaster->skill_q();
    delete pmaster;    

    Master *pmaster1  = new Karthus;
    pmaster1->skill_q();
    delete pmaster1;   

    return 0;
}

输出

skill q belong to master!
skill q belong to karthus!

纯虚函数
如果一个函数被声明为纯虚函数,则说明该类为抽象类,无法创建对象、也无法实例化(纯虚函数没有函数体,不是完整的函数)。抽象类通常是作为基类,让派生类去实现纯虚函数,抽象类通常作为对外接口,不暴露实现。

virtual void skill_w()=0;  //该函数需要在子类中实现

5. 常量函数

在类的成员函数后面加上const,则该函数则被成为常量函数,常量函数的声明和实现都需要加上const;常量函数不允许修改成员变量,避免修改对象的内容,可以将函数定义为常量函数;

//声明
void addManaVolume() const;

//实现
void Master::addManaVolume() const
{
    std::cout<<this->m_blood_volume<<std::endl;        
}

常量函数不允许调用非常量函数;
常量函数可以调用常量函数;

void Master::addManaVolume() const
{    
    //showBloodVolume();   //不允许,常量函数不允许调用非常量函数
    reduceManaVolume();    //允许,常量函数可以调用常量函数
    std::cout<<this->m_blood_volume<<std::endl;        
}
void Master::showBloodVolume()
{
    std::cout<<"blood volume is: "<<m_blood_volume<<std::endl;
}
void Master::reduceManaVolume() const
{
    std::cout<<"const test!"<<std::endl;
}

类的对象也可以声明为const,为常对象,常对象只能访问const成员变量和const成员函数;

const Karthus karthus;    

6. 仿函数

仿函数就是仿造的函数,本质还是类,但是有了函数的特性,也叫做函数对象;仿函数需要重载operator()运算符;

举例说明仿函数的作用:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

class MyFunc
{
public:
    bool operator()(int32_t x, int32_t y){return x > y;}
};

int main(int argc, char *argv[])
{
    std::vector<int32_t> v = {12, 11, 3, 4, 5, 23};
    MyFunc myfunc;
    std::sort(v.begin(), v.end(), myfunc);    
    for(auto au : v)
    {
        std::cout<<au<<" ";
    }
    std::cout<<std::endl;
}

当然也可以通过lambda表达式来实现

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

int main(int argc, char *argv[])
{
    std::vector<int32_t> v = {12, 11, 3, 4, 5, 23};
    std::sort(v.begin(), v.end(), [](int32_t x,int32_t y){return x > y;});
    for(auto au : v)
    {
        std::cout<<au<<" ";
    }
    std::cout<<std::endl;
}

7. 继承

继承允许一个类根据另外一个类来定义,继承可以实现代码的复用;
继承方式

class <派生类名> : <继承方式(public/protected/private)> <基类名>
  • public:子类可以访问基类中的public和protected成员,其属性不变,不能访问private成员;
  • protected:基类中的public和protect成员将成为子类中的protected成员;
  • private:基类中的public和protect成员将成为子类中的private成员;

使用

class Master
{
public:
    Master() = default;
    //Master();     //默认构造函数
	Master(int32_t bloodVolume, int32_t manaVolume) : m_blood_volume(bloodVolume), m_mana_volume(manaVolume){};     //构造函数
    ~Master();      //默认析构函数
private:
    Master(const Master &)=delete;    //默认拷贝构造函数
    Master(Master &&)=delete;    //默认移动构造函数
    Master &operator=(const Master &)=delete;   //默认赋值函数    
    Master &operator=(Master &&)=delete;    //默认移动赋值构造函数

public:
    virtual void skill_q();
    virtual void skill_w();
    void skill_e();
    void skill_r();

    void addBloodVolume();
    void reduceBloodVolume();
    //void showBloodVolume() = delete;
    void showBloodVolume();
    void setBloodVolume(int32_t bloodVolume)
    {
        //m_blood_volume = bloodVolume;
        this->m_blood_volume = bloodVolume;
    }

    void addManaVolume() const;
    void reduceManaVolume() const;
    void showManaVolume();
    void setManaVolume(int32_t manaVolume)
    {
        m_mana_volume = manaVolume;
    }
public:
    static int32_t m_count;

private:
    int32_t m_blood_volume;
    int32_t m_mana_volume;
};

class Karthus : public Master
{
public:
    Karthus() = default;
    ~Karthus(){};
public:
    void skill_q();
    void skill_w();
    void skill_e();
    void skill_r();
};

一般情况下,继承方式会使用public;例如基类中的缓存队列,子类可以通过public的成员函数进行存储和获取;

8. 多态

什么是多态,多态就是让基类的指针可以访问子类的成员函数,基类中的方法需要定义为虚函数(virtual);

基类可以调用自身的方法,可以调用子类的方法,表现为多种形态,即为多态;

如果不使用多态的话,类Karthus、Syndra 和 Ryze都需要定义一遍,如果使用多态只需要将基类的指针指向子类即可;

头文件

#include <iostream>
#include <string>

class Master
{
public:
    Master() = default;    
	Master(int32_t bloodVolume, int32_t manaVolume) : m_blood_volume(bloodVolume), m_mana_volume(manaVolume){};     //构造函数
    ~Master();      //默认析构函数
private:
    Master(const Master &)=delete;    //默认拷贝构造函数
    Master(Master &&)=delete;    //默认移动构造函数
    Master &operator=(const Master &)=delete;   //默认赋值函数    
    Master &operator=(Master &&)=delete;    //默认移动赋值构造函数

public:
    virtual void skill_q();
    virtual void skill_w();
    virtual void skill_e();
    virtual void skill_r();

    void addBloodVolume();
    void reduceBloodVolume();    
    void showBloodVolume();
    void setBloodVolume(int32_t bloodVolume)
    {        
        this->m_blood_volume = bloodVolume;
    }

    void addManaVolume() const;
    void reduceManaVolume() const;
    void showManaVolume();
    void setManaVolume(int32_t manaVolume)
    {
        m_mana_volume = manaVolume;
    }
public:
    static int32_t m_count;

private:
    int32_t m_blood_volume;
    int32_t m_mana_volume;
};

class Karthus : public Master
{
public:
    Karthus() = default;    
    ~Karthus(){};
public:
	//以下函数在.cc中实现
    void skill_q();
    void skill_w();
    void skill_e();
    void skill_r();    
};


class Syndra : public Master
{
public:
    Syndra() = default;
    ~Syndra() {}
public:
    void skill_q(){std::cout<<"skill q belong to Syndra!"<<std::endl;}
    void skill_w(){std::cout<<"skill w belong to Syndra!"<<std::endl;}
    void skill_e(){std::cout<<"skill e belong to Syndra!"<<std::endl;}
    void skill_r(){std::cout<<"skill r belong to Syndra!"<<std::endl;}    
};

class Ryze : public Master
{
public:
    Ryze() = default;
    ~Ryze(){}
public:
    void skill_q(){std::cout<<"skill q belong to Ryze!"<<std::endl;}
    void skill_w(){std::cout<<"skill w belong to Ryze!"<<std::endl;}
    void skill_e(){std::cout<<"skill e belong to Ryze!"<<std::endl;}
    void skill_r(){std::cout<<"skill r belong to Ryze!"<<std::endl;}
};

main.cc

/* class test
*  g++ -g  class.h class.cc main.cc -o d -std=c++11
*/

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

#include "class.h"

int main(int argc, char *argv[])
{
    Master *master = new Karthus;
    master->skill_q();
    master->skill_w();
    master->skill_e();
    master->skill_r();
    std::cout<<std::endl;
    
    master = new Syndra;
    master->skill_q();
    master->skill_w();
    master->skill_e();
    master->skill_r();
    std::cout<<std::endl;

    master = new Ryze;
    master->skill_q();
    master->skill_w();
    master->skill_e();
    master->skill_r();
    std::cout<<std::endl;   

    return 0;
}

输出

skill q belong to karthus!
skill w belong to karthus!
skill e belong to karthus!
skill r belong to karthus!

skill q belong to Syndra!
skill w belong to Syndra!
skill e belong to Syndra!
skill r belong to Syndra!

skill q belong to Ryze!
skill w belong to Ryze!
skill e belong to Ryze!
skill r belong to Ryze!

9. void

void一般会翻译为无、空白等,这里主要介绍void作为参数的使用,void可以指向任何类型的地址

将多态main.cc的输出函数改一下

void show(void *ptr)
{
    //Master *pMaster = (Master*)ptr;
    Master *pMaster = reinterpret_cast<Master*>(ptr);    

    pMaster->skill_q();
    pMaster->skill_w();
    pMaster->skill_e();
    pMaster->skill_r();
    std::cout<<std::endl;    
}

int main(int argc, char *argv[])
{
    Master *master = new Karthus;   
    show(master);
    
    master = new Syndra;    
    show(master);

    master = new Ryze;   
    show(master);

    return 0;
}

10. this指针

this是一个const指针,只能在类的内部使用,可以访问所有的成员变量和函数(public/private/protect)

void setBloodVolume(int32_t bloodVolume)
{
    //m_blood_volume = bloodVolume;
    this->m_blood_volume = bloodVolume;
}

11. 静态成员变量与静态成员函数

静态成员变量
静态成员变量在定义的时候需要加上static,是一种特殊的成员变量,无论该类创建多少个对象,静态成员变量只有1个,且共享

声明

class Karthus : public Master
{
public:
    Karthus() = default;
    ~Karthus(){};
public:
    void skill_q();
    void skill_w();
    void skill_e();
    void skill_r();

    void showCount()
    {
        std::cout<<"m_count value is: "<<m_count<<std::endl;
    }
private:
    static int32_t  m_count;
};

初始化(必须在类外进行初始化),静态成员变量内存的分配是在初始化的时候,且分配一次;

//main.cc文件中

int32_t Karthus::m_count = 36;

使用(直接访问,通过对象或指针)

std::cout<<Karthus::m_count<<std::endl;   //直接访问,当然m_count的属性需要为public

Karthus karthus;    
karthus.showCount();  //通过对象访问

静态成员函数
静态成员函数在定义的时候需要加上static,其没有this指针,只能访问静态成员变量或静态成员函数

定义

class Karthus : public Master
{
public:
    Karthus() = default;
    ~Karthus(){};
public:
    void skill_q();
    void skill_w();
    void skill_e();
    void skill_r();

    static void skill_g();
    static void skill_h();

    void showCount()
    {
        std::cout<<"m_count value is: "<<m_count<<std::endl;
    }
private:
    static int32_t  m_count;
};
void Karthus::skill_g()
{
    m_count++;    
    std::cout<<"m_count(g) value is: "<<m_count<<std::endl;
}

void Karthus::skill_h()
{
    //skill_e();    //不允许,会报错
    skill_g();
    m_count++;    
    std::cout<<"m_count(h) value is: "<<m_count<<std::endl;
}

使用

int main(int argc, char *argv[])
{
	Karthus karthus;    
    karthus.showCount();
    karthus.skill_g();
    karthus.skill_h();
    
    return 0;
}

输出

m_count value is: 36
m_count(g) value is: 37
m_count(g) value is: 38
m_count(h) value is: 39

12. 关键字override和final

override
override关键字,会让编译器检查是否正确覆写父类的虚函数;建议子类中需要覆写的虚函数加上override,这是个好习惯

实例

class Student
{
public:
    Student() = default;
    ~Student(){}
public:
    virtual void showInfo()
    {
        std::cout<<"the class is Student."<<std::endl;
    }
};

class Girl : public Student
{
public:
    Girl() = default;
    ~Girl(){}
public:
    void showInfo()
    {
        std::cout<<"the class is Girl."<<std::endl;
    }
};

int main(int argc, char *argv[])
{
    Student *ptr = new Girl;
    ptr->showInfo();
    return 0;
}

输出

the class is Girl.

修改class Student的showInfo,修改内容如下所示

class Student
{
public:
    Student() = default;
    ~Student(){}
public:
    virtual void showInfo() const
    {
        std::cout<<"the class is Student."<<std::endl;
    }
};

输出

the class is Student.

如果加上override,则会报错,提示没有覆写基类中的showInfo()

class Student
{
public:
    Student() = default;
    ~Student(){}
public:
    virtual void showInfo() const
    {
        std::cout<<"the class is Student."<<std::endl;
    }
};

class Girl : public Student
{
public:
    Girl() = default;
    ~Girl(){}
public:
    void showInfo() override
    {
        std::cout<<"the class is Girl."<<std::endl;
    }
};

final
final表示该类禁止被继承,该关键字在使用的时候需要特别注意;
以下代码无法编译通过,Student无法被继承

class Student final
{
public:
    Student() = default;
    ~Student(){}
public:
    virtual void showInfo()
    {
        std::cout<<"the class is Student."<<std::endl;
    }
};

class Girl : public Student
{
public:
    Girl() = default;
    ~Girl(){}
public:
    void showInfo() override
    {
        std::cout<<"the class is Girl."<<std::endl;
    }
};

13. 类模板

创建一个通用类,类中使用的类型可以先不指定;在创建类对象的时候再指定;
模板是泛型编程的基础,这里只是列举一下类模板的例子

template<class T1, class T2>
class Man
{
public:
    void setValue(T1 name,T2 age)
    {
        m_nmae = name;
        m_age = age;
    }

    void showInfo()
    {
        std::cout<<"name is: "<<m_nmae<<std::endl;
        std::cout<<"age is: "<<m_age<<std::endl;
    }

private:
    T1 m_nmae;
    T2 m_age;
};


int main(int argc, char *argv[])
{
    Man<std::string, int32_t> man;
    man.setValue("xiaoming",18);
    man.showInfo();

    Man<int32_t, int32_t> man1;
    man1.setValue(01, 20);
    man1.showInfo();
	return 0}

输出

name is: xiaoming
age is: 18
name is: 1
age is: 20
class Person
{
public:
    void show()
    {
        std::cout<<"person"<<std::endl;
    }
};

template<class T>
class Boy
{
public:
    void show()
    {
        t.show();
    }

private:
    T t;
};

int main(int argc, char *argv[])
{
    Boy<Person> boy;
    boy.show();
    return 0;
}

输出

person
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐