C++类与对象小结

类与对象

类是创建对象的模板,一个类可以创建多个对象,每个对象都是类类型的一个变量;创建对象的过程也叫类的实例化。

将类的成员函数称为类的方法(Method)。

在面向对象的编程语言中,经常把函数(Function)称为方法(Method)。

类的定义

class 是 C++ 中新增的关键字,专门用来定义类

class 类名
{
public:
    // 成员变量
    
    // 成员函数
};

最后的;很重要,注意检查,防止没必要的错误

public 也是 C++ 的新增关键字,它只能用在类的定义中,表示类的成员变量或成员函数具有“公开”的访问权限

创建对象

创建类:

class Student
{
public:
    string name;
    int age;
    void say()
    {
        cout << name << "说:我今年"
            << age << "岁了!" << endl;
    }
};

创建对象

class 类名 对象名;

或

类名 对象名;

例如:
class Student LiMing;
Student LiMing;

在创建对象时,class 关键字可要可不要,Student是类名,LiMing是对象名

除了创建单个对象,还可以创建对象数组

Student AllStu[100];        // 这就创建类容量为100的对象数组

访问类的成员

创建对象以后,可以使用点号.来访问成员变量和成员函数

// 使用成员变量
    LiMing.name = "李明";
    LiMing.age = 25;

// 使用成员函数
    LiMing.say();

使用对象指针

创建的对象 stu 在 栈上 分配内存,指针执行那个该对象,需要使用 取地址符 &获取它的地址

Student stu;
Student *pStu = &stu;

pStu 是一个指针,它指向 Student 类型的数据,也就是通过 Student 创建出来的对象。

需要使用 new 关键字

类名* 对象名 = new 类名;

例如:
Student *pStu = new Student;

使用 new 在 堆上 创建出来的对象是匿名的,没法直接使用,必须要用一个指针指向它,再借助指针来访问它的成员变量或成员函数。

对象指针,可以通过箭头 -> 来访问对象的成员变量和成员函数

栈内存是程序自动管理的

堆内存由程序员管理,对象使用完毕后可以通过手动释放

new 和 delete 往往成对出现,以保证及时删除不再使用的对象,防止无用内存堆积。

Student* LiLei = new Student;
LiLei->name = "李雷";
LiLei->age = 63;
LiLei->say();

例子:

#include 
#include 
using namespace std;
class Student
{
public:
// 成员变量
    string name;
    int age;
// 成员函数
    void say()
    {
        cout << name << "说:我今年"
            << age << "岁了!" << endl;
    }
};
int main(int argc,char** argv)
{
// 创建实例对象
    Student LiMing;
    LiMing.name = "李明";
    LiMing.age = 25;
    LiMing.say();
    
// 使用指针创建实例对象
    Student* LiLei = new Student;
    LiLei->name = "李雷";
    LiLei->age = 63;
    LiLei->say();
    delete LiLei;// 释放空间
    return 0;
}

匿名对象

格式:

(new 类名) -> 成员函数或成员变量;

例如:
(new Student)->name = "小明";
(new Student)->age = 23;

每个匿名对象互不影响,互不关联,都是单独的个体

成员函数

类这种数据类型是一个包含成员变量和成员函数的集合。

成员函数和普通函数类似,都有返回值和参数列表,但作用域不同

成员函数可以的定义在类体内,也可以定义在类外

class Student
{
public:
// 成员函数,在类体内部声明
    void say()
    {
        cout << "Hello World!" << endl;
    }
};

在类体外声明,需要用到 域解析符(也称作用域运算符或作用域限定符) ::

格式:
class 类名
{
    数据类型 函数名(参数列表);
}
(inline) 数据类型 类名::函数名(参数列表)
{
    // 函数体
}

例如:
class Student
{
public:
    void say();
};
void Student::say()
{
    cout << "Hello World!" << endl;
}

inline 是可选的,可根据具体情况而定

在类体外定义 内联 (inline) 函数的方式,必须将类的定义和成员函数的定义都放在同一个头文件中(或者同一个源文件中),否则编译时无法嵌入到类中

类成员的权限访问

成员函数和成员变量的访问权限修饰符:

  • public
  • private
  • protected

(private)私有成员变量和成员函数只能在类内部使用,在类外都是无效的

成员函数和成员变量无论使用何种修饰符修饰,在类内部,均可以随意使用

在类外部,只能通过声明的实例对象访问访问 public 属性的成员,不能访问 private、protected 属性的成员

class Student
{
// 私有化,只能在类内部使用
private:
    int age;
// 保护模式,只能在类及 子类(继承) 中使用
protected:
    string name;
// 公共的,可以任意访问
public:
    void say()
    {
        cout << name << "今年" << age << "岁!" << endl;
    }
};

一般情况下,需要将类的成员变量隐藏(private),外部不能直接修改,需要使用特定的接口来set函数,使用 get函数来获取,(在英语中,可以知道 set-设置 ,get-获取, 语义化,更容易理解)

设置成员变量的函数的名字通常以set开头,后跟成员变量的名字,所以通常称为 set 函数。

读取成员变量的函数的名字通常以get开头,后跟成员变量的名字,所以通常称为 get 函数。

class Student
{
private:
    int age;
public:
    void setAge(int age)
    {
        age = age;
    }
    int getAge()
    {
        return age;
    }
};

在setAge中,存在两个age,会有些分不清,会产生混淆,因此在定义私有变量命名时,使用特殊格式,以 m_开头,例: m_age

class Student
{
private:
    int m_age;
public:
    void setAge(int age)
    {
        m_age = age;
    }
    int getAge()
    {
        return m_age;
    }
};

如果不写任何修饰符,就会 默认为 private

声明为 private 的成员和声明为 public 的成员的次序任意,既可以先出现 private 部分,也可以先出现 public 部分。

this指针

this 是 C++ 中的一个关键字,也是一个 const 指针,它指向当前对象,通过它可以访问当前对象的所有成员。

this 只能用在类的内部,通过 this 可以访问类的所有成员,包括 private、protected、public 属性的。

this 是一个指针,要用->来访问成员变量或成员函数

class Student
{
private:
    int age;
public:
    void setAge(int age)
    {
        this->age = age;
    }
}

使用this,可以清晰的区分参数,有助于理解类的作用

this 实际上是成员函数的一个形参,在调用成员函数时将对象的地址作为实参传递给 this。不过 this 这个形参是隐式的,它并不出现在代码中,而是在编译阶段由编译器默默地将它添加到参数列表中。

this 作为隐式形参,本质上是成员函数的局部变量,所以只能用在成员函数的内部,并且只有在通过对象调用成员函数时才给 this 赋值。

#include 
#include 
using namespace std;

class Student
{
// 成员变量
private:
    int age;
    string name;
public:
// 成员函数
    void setAge(int age)
    {
    // 设置age
        this->age = age;
    }
    void setName(string name)
    {
    // 设置name
        this->name = name;
    }
    int getAge()
    {
    // 获取age
        return this->age;
    }
    string getName()
    {
    // 获取name
        return this->name;
    }
    void say()
    {
        cout << this->name << "说:来,骗,来,偷袭," << this->age << "岁的老同志" << endl;
    }
};

int main(int argc,char** argv)
{
    Student Sir;
    Sir.setName("马保国");
    Sir.setAge(69);
    Sir.say();
    return 0;
}

结果:
马保国说:来,骗,来,偷袭,69岁的老同志

构造函数

构造函数 : 名字和类名相同,没有返回值,不需要用户显式调用(用户也不能调用),而是在创建对象时自动执行

构造函数 :在创建对象的同时为成员变量赋值

默认情况下,系统会默认给类添加一个构造函数

Student(){}

当已存在构造函数,系统就不会添加默认的构造函数

不管是声明还是定义,构造函数名前面都不能出现返回值类型,即使是 void 也不允许;

#include 
#include 
using namespace std;
class Student
{
private:
// 成员变量
    int age;
    string name;
public:
// 构造函数
    Student(int age,string name){
        this->age = age;
        this->name = name;
    }
// 成员函数
    void say()
    {
        cout << this->name << "说:来,骗,来,偷袭," << this->age << "岁的老同志" << endl;
    }
};

int main(int argc, char** argv)
{
    Student Sir(69,"马保国");
    Sir.say();
    return 0;
}


结果:
马保国说:来,骗,来,偷袭,69岁的老同志

一个类可以存在多个构造函数

构造函数的调用是强制性的,一旦在类中定义了构造函数,那么创建对象时就一定要调用,不调用是错误的,防止出现错误,一般可以手动添加一个 无参 构造函数

创建对象时只有一个构造函数会被调用

#include 
#include 
using namespace std;
class Student
{
private:
    int age;
    string name;
public:
// 无参构造函数
    Student() {
        this->age = 69;
        this->name = "老同志";
    }
// 构造函数
    Student(int age,string name){
        this->age = age;
        this->name = name;
    }
// 成员函数
    void say()
    {
        cout << this->name << "说:来,骗,来,偷袭," << this->age << "岁的老同志" << endl;
    }
};

int main()
{
    Student Sir;
    Sir.say();
    return 0;
}

结果:
老同志说:来,骗,来,偷袭,69岁的老同志

也可以使用 对象指针

Student* Sir = new Student();
Sir->say();
delete Sir;

Student* Man = new Student(69,"马保国");
Man->say();
delete Man;

构造函数也可以定义在 类体 外

class Student
{
private:
    int age;
    string name;
public:
    Student(int age, string name);
};

Student::Student(int age, string name) {
    this->age = age;
    this->name = name;
}

构造函数初始化列表

格式 ( Student 类):

class Student
{
private:
    int m_age;
    string m_name;
public:
    Student(int age, string name);
};

Student::Student(int age, string name):m_age(age),m_name(name) {}

定义构造函数时并没有在函数体中对成员变量一一赋值,其函数体为空(可以有其他语句)

而是在函数首部与函数体之间添加了一个冒号 :,后面紧跟 m_age(age),m_name(name), 语句,这个语句的意思相当于函数体内部的 m_age = age;m_name = name;语句

使用构造函数初始化列表并没有效率上的优势,仅仅是书写方便

成员变量的初始化顺序与初始化列表中列出的变量的顺序无关,它只与成员变量在类中声明的顺序有关。

构造函数初始化列表还有一个很重要的作用 : 初始化 const 成员变量

class Student
{
private:
    const int m_len = 0;
public:
    Student(int len);
    void say()
    {
        cout << this->m_len << endl;
    }
};
Student::Student(int len): m_len(len){}

析构函数

创建对象时系统会自动调用构造函数进行初始化工作,同样,销毁对象时系统也会自动调用一个函数来进行清理工作,例如释放分配的内存、关闭打开的文件等,这个函数就是 析构函数

析构函数(Destructor)也是一种特殊的成员函数,没有返回值,不需要程序员显式调用(程序员也没法显式调用),而是在销毁对象时自动执行。

  • 构造函数的名字和类名相同,而析构函数的名字是在类名前面加一个~ 符号。
  • 析构函数没有参数,不能被重载,因此一个类只能有一个析构函数。
  • 如果没有定义,编译器会自动生成一个默认的析构函数。
例如:
~Student(){}

用 new 分配内存时会调用构造函数,用 delete 释放内存时会调用析构函数。

class Student
{
private:
    int m_len;
    int* arr;
public:
    Student(int len);
    ~Student();
};

Student::Student(int len):m_len(len){
    // 在堆上创建数组
    if( len > 0) this->arr = new int[len];
    else  this->arr = new int[10];
}
Student::~Student()
{
    delete arr; // 释放数组内存
}

new 创建的 对象 位于 堆区 ,通过 delete 删除时才会调用析构函数;如果没有 delete,析构函数就不会被执行。

#include 
using namespace std;

class Student
{
private:
    int m_len;
public:
    Student(int len);
    ~Student();
};
Student::Student(int len):m_len(len){}
Student::~Student()
{
    cout << this->m_len << endl;
}

int main(int argc, char** argv)
{
    Student* Man = new Student(1);
    Student* Woman = new Student(2);
    
    delete Man;
    
    return 0;
}

输出结果为:
1

静态成员

静态成员变量

静态成员变量是一种特殊的成员变量,它被关键字 static 修饰

例如:
class Student
{
public:
    static int count;
};

static 成员变量属于类,不属于某个具体的对象,即使创建多个对象,只会 分配一份内存,所有对象使用的都是这份内存中的数据。

静态成员变量,不能通过构造函数初始化,因为其不是类的非静态成员

静态成员变量初始化,需要在类外完成

访问静态成员变量的方式:

//通过类类访问 static 成员变量
Student::count = 10;

//通过对象来访问 static 成员变量
Student stu;
stu.count = 20;


//通过对象指针来访问 static 成员变量
Student *pstu = new Student;
pstu -> count = 20;

static 成员变量不占用对象的内存,而是在所有对象之外开辟内存,即使不创建对象也可以访问。

在类内部可以访问静态成员变量,也可以使用this指针

#include 
#include 

using namespace std;

class Student
{
private:
    string m_name;
public:
    static int count;
public:
    Student(string name):m_name(name){
        count++;
    };
    void say()
    {
        // 类内部访问 静态成员变量
        cout << "第 " << this->count << " 位同学: " << this->m_name << endl;
    }
};
// 初始化静态成员变量
int Student::count = 0;

int main()
{
    Student s1("小明");
    cout << s1.count << endl;
    s1.say();

    Student* s2 = new Student("李雷");
    cout << s2->count << endl;
    s2->say();
    delete s2;

    cout << "全班有" << Student::count << "位同学" << endl;
    return 0;
}

结果:
1
第 1 位同学: 小明
2
第 2 位同学: 李雷
全班有2位同学
  • 一个类中可以有一个或多个静态成员变量,所有的对象都共享这些静态成员变量
  • static 成员变量和普通 static 变量一样,都在内存分区中的全局数据区分配内存,到程序结束时才释放。

static 成员变量不随对象的创建而分配内存,也不随对象的销毁而释放内存。

  • 静态成员变量必须初始化,而且只能在类体外进行
  • 静态成员变量既可以通过对象名访问,也可以通过类名访问,但要遵循 private、protected 和 public 关键字的访问权限限制。当通过对象名访问时,对于不同的对象,访问的是同一份内存。

静态成员函数

静态成员函数只能访问静态成员

静态成员函数没有 this 指针,函数内部不能使用this指针

class Student
{
public:
    static int count;
public:
    static void say()
    {
        cout << count << endl;
        // cout << this->count << endl; 错误的格式
    }
    void run()
    {
        this->say();    // 可以访问静态成员函数
    }
};

静态成员函数在声明时要加 static,在定义时不能加 static

class Student
{
public:
    static int count;
public:
    static void say();
    void run();
};

void Student::say()
{
    cout << count << endl;
}
void Student::run()
{
    this->say();
}

const成员

const 成员变量,只需在定义时,使用 const 关键字,通过 构造函数 进行初始化

const 成员函数 :称为常成员函数。

const 成员函数可以使用类中的所有成员变量,但是不能修改它们的值

  • 常成员函数 需要在声明和定义的时候在函数头部的 结尾 加上 const 关键字
  • 函数 开头 的 const 用来修饰 函数的返回值
  • 必须在成员函数的声明和定义处同时加上 const 关键字
int getScore() const;


int Student::getScore() const
{
    return m_score;
}
  • 通常将 get 函数设置为常成员函数
#include 
#include 

using namespace std;
class Student
{
private:
    string m_name;
    int  m_score;
public:
    // 构造函数
    Student(string name,int m_score);
    
    // 类体内声明
    string getName() const
    {
        // this->m_age = "小明"; 显示错误,不能使用this指针
        return m_name;
    };
    
    // 类体外声明
    int getScore() const;
};

int Student::getScore() const
{
    return m_score;
}
// 构造函数
Student::Student(string name,int score) : m_name(name),m_score(score) {};

int main()
{
    Student s("李雷",75);
    cout << s.getName() << endl;
    cout << s.getScore() << endl;
    return 0;
}

const 对象

const用来修饰对象,称为常对象

一旦将对象定义为 常对象 之后,就 只能调用类的 const 成员(包括 const 成员变量和 const 成员函数)

const 类名 对象名;

例如:
const Student Sir;

可以定义 const 指针

const Student* Sir = new Student;

例如:

#include 
#include 

using namespace std;

class Student
{
private:
    string m_name;
    int  m_score;
public:
    // 构造函数
    Student(string name, int score) : m_name(name), m_score(score) {};
    int getScore() const 
    {
        return m_score;
    }
    void show()
    {
        cout << m_name << "的英语" << m_score << endl;
    }
    string getName() const
    {
        //show(); 错误,不能使用this指针,也不能访问
        cout << "英语" << getScore() << "的";
        return m_name;
    };
};
int main()
{
    const Student s("李雷", 75);
    cout << s.getName() << endl;
    //s.show(); 错误
    
    const Student * sir = new Student("小强",100);
    cout << sir->getName() << endl;
    sir->show(); 错误
    return 0;
}

友元函数

借助友元(friend),可以使得 其他类 中的 成员函数 以及全局范围内的函数 访问 当前类的 private 成员

使用关键字 friend , 借助 友元 可以访问与其有好友关系的类中的私有成员

注意,友元函数不同于类的成员函数,在友元函数中不能直接访问类的成员,必须要借助对象。

参数 传递对象(可以直接传递对象,也可以传递对象指针或对象引用),并在访问成员时指明对象。

将非成员函数声明为友元函数

注意:

  • 友元函数的定义,一定要在类的定义之后,否则无法访问
#include 
#include 

using namespace std;
class Student
{
private:
    string m_name;
    string m_work;
public:
    // 构造函数
    Student(string name, string work) :m_name(name), m_work(work) {};
public:
    // 友元函数
    friend void show(Student* s);
    friend void say(Student s);
};

void show(Student* s)
{
    cout << "朋友们好啊, 我是" << s->m_work << s->m_name << endl;
}
void say(Student s)
{
    cout << "大家好, 我是" << s.m_work << s.m_name << endl;
}

int main()
{
    Student mbg("马保国","浑元形意太极门掌门人");
    show(&mbg); 
    
    Student cxk("坤坤","练习两年半的偶像练习生");
    say(cxk);
    return 0;
}

结果:
朋友们好啊, 我是浑元形意太极门掌门人马保国
大家好, 我是练习两年半的偶像练习生坤坤

将其他类的成员函数声明为友元函数

friend类 表示 拥有友元函数(friend)的类

member类 表示 拥有成员函数的类

类之间的友元函数,需要注意类的顺序,以及成员函数定义的位置和方式:

  1. member类 要先定义,之后再定义 friend类

下例: 由于 类 Introduct 中的成员函数要 作为 类 Student 的友元函数,因此可以访问类 Student 的成员 ,所以 类 Introduct 需要先定义

  1. 类必须在正式声明之后才能使用;但是某些情况下,只要做好提前声明,也可以先使用。由于 friend类,在 member类 之后定义,当执行到 member类 时,需要用到friend类 ,但是 friend类 没有定义, 因此需要 提前声明

下例:

class Student;          // 很有必要的提前声明
  1. member类 的成员函数 声明实现 要分开,需要将 实现 方式放在 friend类 的定义之后
  • friend类 对应的就是 出现 friend 的类,共享自己的数据
定义的一般顺序:

第一步:
class friend;

第二步:
class member{
    // 成员变量 , 成员函数
    // 需要 友元的成员函数 声明
}

第三步:
class friend{
    // 成员变量 , 成员函数
    // friend member类的 友元成员函数
}

第四步:
member类的 友元成员函数 的实现
#include 
#include 

using namespace std;

class Student;          // 很有必要的提前声明

// 第一个类, member类
class Introduct
{
private:
    static int count;
public:
    // 构造函数
    Introduct() { count++; };
public:
    // 成员函数
    void show(Student* s);
    void say(Student s);
};

// 第二个类, friend类
class Student
{
private:
    string m_name;
    string m_work;
public:
    // 构造函数
    Student(string name, string work) :m_name(name), m_work(work) {};
public:
    // 友元函数
    friend void Introduct::show(Student* s);
    friend void Introduct::say(Student s);
};

// 第一个类的成员函数,即第二个类的友元函数
void Introduct::show(Student* s)
{
    cout << this->count << ". 朋友们好啊, 我是" << s->m_work << s->m_name << endl;
}
void Introduct::say(Student s)
{
    cout << this->count << ". 大家好, 我是" << s.m_work << s.m_name << endl;
}

int Introduct::count = 0;
int main()
{
    Student mbg("马保国","浑元形意太极门掌门人");
    (new Introduct)->show(&mbg);                    // 匿名对象

    Student cxk("坤坤","练习两年半的偶像练习生");
    (new Introduct)-> say(cxk);                    // 匿名对象
}

结果:
1.朋友们好啊, 我是浑元形意太极门掌门人马保国
2.大家好, 我是练习两年半的偶像练习生坤坤

友元类

将类 B 声明为类 A 的友元类,那么类 B 中的所有成员函数都是类 A 的友元函数,可以访问类 A 的所有成员,包括 public、protected、private 属性的。

除非有必要,一般不建议把整个类声明为友元类,而只将某些成员函数声明为友元函数

friend类 表示 拥有友元函数(friend)的类

member类 表示 拥有成员函数的类

注意:

  • 优先定义 friend类,其次是 member类
  • friend类 对应的就是 出现 friend 的类,共享自己的数据
#include 
#include 

using namespace std;

// 第一个类, friend类
class Introduct
{
private:
    static int count;
public:
    // 构造函数
    Introduct() { count++; };
public:
    // 友元类
    friend class Student;
};

// 第二个类, member类
class Student
{
private:
    string m_name;
    string m_work;
public:
    // 构造函数
    Student(string name, string work) :m_name(name), m_work(work) {};
public:
    // 友元函数
    void show(Introduct* s);        // 类体外
    void say(Introduct s)           // 类体内
    {
        cout << s.count << ". 大家好, 我是" << this->m_work << this->m_name << endl;
    };
};

void Student::show(Introduct* s)
{
    cout << s->count << ". 朋友们好啊, 我是" << this->m_work << this->m_name << endl;
}

// 静态成员变量
int Introduct::count = 0;
int main()
{
    Student mbg("马保国","浑元形意太极门掌门人");
    Introduct mg;
    mbg.show(&mg);

    Student cxk("坤坤","练习两年半的偶像练习生");
    Introduct kk;
    cxk.say(kk);
    
    return 0;
}

结果:
1.朋友们好啊, 我是浑元形意太极门掌门人马保国
2.大家好, 我是练习两年半的偶像练习生坤坤

友元的关系不能传递。如果类 B 是类 A 的友元类,类 C 是类 B 的友元类,不等于类 C 是类 A 的友元类, 类C 不能使用 类 A 的成员

友元的关系是单向的而不是双向的。如果声明了类 B 是类 A 的友元类,不等于类 A 是类 B 的友元类,类 A 中的成员函数不能访问类 B 中的 private 成员。

class与struct

struct 是 C语言 的关键字

在C语言中,struct 只能包含成员变量,不能包含成员函数。而在C++中,struct 类似于 class,既可以包含成员变量,又可以包含成员函数。

struct 和 class 基本是通用的,仍存在一些区别:

  1. 使用 class 时,类中的成员默认都是 private 属性的;而使用 struct 时,结构体中的成员默认都是 public 属性的。
  2. class 继承默认是 private 继承,而 struct 继承默认是 public 继承
  3. class 可以使用模板,而 struct 不能

例如:


#include 
#include 

using namespace std;

// 使用  "struct" 结构体
struct Student
{
    int number = 6;
    static int count;
private:
    // 成员变量
    string m_name;
    string m_work;
public:
    // 构造函数
    Student(string name, string work) :m_name(name), m_work(work) { 
        // 计数
        count++; 
    };
    // 析构函数
    ~Student() { cout << this->m_name <count << ". 大家好, 我是" << this->m_work << this->m_name << endl;
    }
};

void Student::show()
{
    cout << this->count << ". 朋友们好啊, 我是" << this->m_work << this->m_name << endl;
}

// 静态成员变量初始化
int Student::count = 0;
int main()
{
    Student mbg("马保国","浑元形意太极门掌门人");
    mbg.show();
    Student cxk("坤坤","练习两年半的偶像练习生");
    cxk.say();
    // 默认为 public ,因此可以访问number
    cout << cxk.number << endl;
    
    return 0;
}

结构:
1. 朋友们好啊, 我是浑元形意太极门掌门人马保国
2. 大家好, 我是练习两年半的偶像练习生坤坤
6
坤坤
马保国

你可能感兴趣的:(C++类与对象小结)