C++基础的不能再基础的学习笔记——类(一)

本文深入探讨C++中类的定义与使用,包括成员函数、构造函数等关键概念,并介绍封装、友元等特性,帮助读者理解面向对象编程的核心。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在C++中,我们用类来定义自己的数据类型。类的基本思想是数据抽象封装,可以理解为将接口与实现分开,提供给用户可使用的类的接口,但类的具体实现对用户是透明的。

class Person {

private:
    string name;
    unsigned age = 0;
    bool sex = 0;

public:
    Person() {name = "";age = 0;sex = 0;};

    void SetName(string s) { this->name = s; };
    string GetName() const { return this->name; };
    person& GetInfo() { return *this; };

};
一、函数

类的函数分为 类的成员函数 和 类的非成员函数。

类的成员函数

类的成员函数必须在类内声明,但根据需要在类内或类外定义,在类内定义的函数是隐式的内联(inline)函数,即在编译过程中会在调用点进行展开的函数。

1. 常量指针this
Person fancy;

fancy.SetName("MaYun");

现在我们定义了一个person类型的对象fancy,通过fancy.SetName("MaYun")来设置成员变量name的值。

需要注意的是,当我们通过对象来调用成员函数的时候,实际上都是在调用该对象的成员函数

那么如何确定这个对象呢?成员函数通过一个名为this的隐式参数来访问调用它的对象,当我们调用函数时,要将对象的地址传递给this。因此,fancy.SetName("MaYun")实际上是做了Person::SetName(&fancy,"MaYun")的操作。

2. const成员函数

常量成员函数是一种操作对象的过程中,保证对象不改变的一种函数。

string GetName() const中的const关键字是用来修改隐式this指针的类型的。this指针的默认类型为 Type *const,无法指向常量对象(常量对象不允许通过指针进行修改) ,而const修饰的成员函数,this指针的类型为const Type *const(此时不可通过this指针改变对象值),也就是做到了保证不改变对象值。

3. 返回this对象的函数
Person& GetInfo() { return *this; };
Person GetInfo() { return *this; };

const Person& GetInfo() const { return *this; };

上述代码的第一个函数,返回类型为Person类型的引用,返回的是对象本身

第二个函数,返回类型为Person类型,此时返回的是对象的一个副本

第三个函数为一个常函数,this的类型为const Person *const,因此 *this的类型为const Person,所以常函数的返回类型也应为const Person。

4. 基于const的重载

我们可以利用函数是否为const来进行重载。

Person& display() 
{ 
    cout << "1." << name << endl;  
    return *this; 
};
const Person& display() const 
{ 
    cout << "2." << name << endl;
    return *this; 
 };


const Person sixday("six", 23, 1);

fancy.display();    //调用非常量函数
sixday.display();   //调用常量函数

在上述代码中,定义了两个display函数,一个为常函数,一个不是常函数。而fancy不是一个常量对象,当它在调用display时,调用的是非常量的;sixday是一个常量对象,当它在调用display时,调用的是常量的。


类的非成员函数

类的非成员函数是对类的操作,但不属于类本身,因此定义和声明都在类外。

那么一个函数,如何判定它应该为成员函数还是非成员函数呢?一般情况下,将与类有关而与单个对象无关的函数声明为非成员函数。非成员函数有friend函数static函数两种。


构造函数

构造函数在一个对象被创建时调用,用于初始化对象的数据成员。

1.默认构造函数

当我们没有为类声明和定义构造函数时,编译器会自动为类生成一个默认构造函数① 根据数据成员的初始值初始化 ② 若无初始值,则进行默认初始化

2.自定义构造函数

在多数情况下,我们不能依赖于默认构造函数,要自定义构造函数,原因有下:

① 默认构造函数只有在类无构造函数时才会生成。

② 默认构造函数可能会执行错误的操作。在数据成员含有内置类型复合类型时(这两种类型的对象,在函数体外定义时,会默认初始化;在函数体内定义时,不会进行初始化),必须进行初始化,若没有定义初始值且调用默认构造函数,可能会得到错误的值。
因此,当数据成员含内置类型和复合类型时,必须初始化。

③ 对于一个包含 无默认构造函数的类A的对象 的类B,编译器无法为类B创建默认的构造函数。

Person() = default;   //默认构造函数
Person(string s, int a, bool se) : name(s), age(a), sex(se) {};

其中通过Person() = default;的方式将其定义为默认构造函数,即在形参列表后加上= default来要求编译器生成默认构造函数。同样的,若= default在类内,则函数为内联函数。

二、友元

对于类的非成员函数,无法访问类的私有成员,但有时我们希望非成员函数有访问权,此时就可以将非成员函数声明为类的友元函数

对于两个类,一个类的对象不可以访问另一个类的私有成员,但有时我们希望可以访问,此时就可以将一个类声明为另一个的友元类

只需在类内以friend关键字声明即可,但友元不是类的成员,它只是获得了对类私有成员的访问权限。

1. 函数重载和友元

即使重载函数的名称是一样的,在声明为类的友元函数时,也必须逐一声明

2. 友元声明和作用域

友元函数可以在类内定义,此时在类外不需要再声明。

class A{
    friend void print(){ cout << "yo~" << endl};
};

而我们一般使用友元函数时,仅仅用于运算符重载,因为友元会破坏封装性。

三、类的声明

我们可以先声明一个类而不定义它,在声明之后定义之前的类称作不完全类型

class Brid; //只声明未定义,不完全类型

但此时,对这个类的使用非常有限:

① 可以定义指向该类型的指针和引用

可以声明(不可以定义)以该类型作为形参或返回类型的函数。

class A;
class B;

class A{
    B* baby;
};

class B{
    A alice;
};

通过以上我们可以看出,封装就是隐藏对象的属性和实现细节,只对外提供可访问的接口

同时也可以看出,封装带来了许多的好处

① 可以防止用户代码破坏数据。若发现数据损坏,则可以在有限的范围内定位缺陷,大大降低了改bug的难度。

属性和实现细节可以随时改变而不影响用户代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值