C++类和对象的细节原理:this指针、构造函数和析构函数、深浅拷贝、运算符重载、初始化列表、类的各种成员和方法

简介: C++类和对象的细节原理:this指针、构造函数和析构函数、深浅拷贝、运算符重载、初始化列表、类的各种成员和方法

相关基础概念

OOP:面向对象编程。OOP语言的四大特征是抽象、封装/隐藏、继承、多态。

实体:比如人,一个人就是一个实体,比如表格,一张表格也可以是一个实体。

属性、行为:比如人的属性有年龄、身高、体重等,人的行为有吃喝拉撒等。

类:实体的抽象类型,比如一个人的类,就是先把人的属性、行为等抽象出来,虚虚地放在代码段上,仅仅是一个模板,类型是不占空间的。当要描述某个人的这个实体的时候,把这个类填充上属性即可,但是多个实体公用一套行为。实体的属性的抽象对应类的成员变量,实体的行为对应类的成员方法。属性一般是私有的,方法一般是公有的。

成员方法的实现方法:有类体内实现和类体外实现两种方法。类体内实现的方法自动转为inline函数。类体外实现需要在函数名称前面加上作用域和作用域解析符。

实例化:把类填充上属性,也叫类的实例化。

对象:类实例化的产物叫做对象。

This指针

类的成员方法一经编译,所有的方法参数,都会加一个this指针,接收调用该方法的对象的地址。

所以C++的good1.init(“面包”)实际上是C语言中的init(&good1,”面包”)。但是在C++中,当你调用一个成员函数时,编译器会自动传递调用该函数的对象的地址作为函数的隐含参数(即 this 指针),因此你无需手动传递对象的地址。通俗来说就是,C++的good1.init(“面包”),看起来没有传入&good1,但是在这个方法内部只要用了this这个关键字,就相当于在用&good1。

构造函数与析构函数

初始化和释放不一定要通过构造和析构函数完成,但是C++中为了保证代码的安全性,设置了构造和析构函数,析构函数能自行执行,构造函数必须自动或显式地执行以此保证我们不会忘记初始化。

构造函数:一般用于初始化对象成员变量。

析构函数:一般用于释放对象成员变量所占的外部堆内存。

构造函数的特点:1、不带返回值。2、与类同名。3、可以有多个形参列表不同的构造函数。4、构造函数会根据我们的传参自行选择是哪个函数。5、对象的创建必定经过构造函数。

析构函数的特点:1、不带返回值。2、~+类名。3、只能有一个。4、不能带参数。5、析构函数可以自行调用(不建议)。5、如果对象创建在堆上,需要自己手动删除,删除的时候回自动调用析构函数。6、一个程序可能会有多个对象需要析构,按反入栈的顺序进行析构。7、一个对象销毁前必定经过析构函数。

拷贝构造函数与深浅拷贝

形参类型是同类(比如const CClass &c)的构造函数就是拷贝构造函数。

CClass c2 = c1;

CClass c3(c1);

这两句代码都是在调用拷贝构造函数,是等价的。

默认情况下拷贝构造函数是把成员变量一一复制,如果有成员变量是地址,指向了堆内的空间,那么复制过来的成员变量指向了同一块堆内存空间,这是浅拷贝。

如果需要把堆内存空间也放到新开辟的堆空间里,我们需要在拷贝构造函数里自定义这个堆空间的开辟和复制过程,也就是深拷贝。

运算符重载

运算符重载(Operator Overloading)是 C++ 中的一种特性,允许你重新定义或扩展已有的运算符的行为,使其适用于自定义的类或数据类型。

比如赋值运算符重载,operator=有点类似一个被定义的函数。

class MyClass {
public:
    int value;
    MyClass& operator=(const MyClass& other) {
        if (this != &other) {
            value = other.value;
        }
        return *this;
    }
};

返回引用是支持连续的operator=赋值操作。

返回引用意味着是除了把*this作为返回值之外,operator=的左边如果是引用类型,那么operator=的左边的变量也是*this的引用。如果不是引用类型,那么返回值被当做右值使用。

构造函数的初始化列表

成员对象:B类变量是A类的成员变量之一,那么可以叫这个B类变量为A类的成员对象。

如果B类自定义了构造函数,那么默认构造函数会被覆盖掉,那么A类对象构造的时候无法自动调用B类对象的构造函数,就需要手动调用。

初始化列表的语法如下:

ClassName::ClassName(Type1 arg1, Type2 arg2, ...) : member1(arg1), member2(arg2), ... {

   // 构造函数的主体

}

初始化列表会省略类型。

假设menber1是int age,在初始化列表中member1(arg1)其实就是int age = arg1;

假设member2是CClass c,member2(arg2)其实就是CClass c(arg2);

注意:成员变量的初始化和他们定义顺序有关,和构造函数初始化列表中出现的先后顺序无关。

类的各种成员、方法

普通成员方法

  1. 属于类的作用域。
  2. 调用该方法的时候,需要依赖一个对象。
  3. 可以任意访问对象的私有成员变量。
  4. 默认传入了this指针。

Static成员变量

  1. 所有同类对象共享,所有对象都能操作它。
  2. 需要类外定义并且初始化。初始化方法:类型 作用域::变量名 = 值。
  3. 它不属于对象,属于类级别。本质是全局变量,但是作用域被限制在了类中。

Static成员方法

1、可以直接用类名::static方法的方式调用。如果每次访问static变量都需要通过某个对象,显然很别扭,因此诞生了static方法,可以不通过对象,而是通过类直接访问static变量。

2、因为调用它不依赖对象,所以它没有传入this指针,不方便访问普通的变量。

Const成员方法

如果创建对象的时候创建成了const类型的对象,那么普通的成员方法因为默认是非const的this指针,无法接受const类型的对象的指针。这时候就需要在普通方法的声明的右括号之后加const修饰,表示this指针形参被改成了const this指针形参。

建议如果是只涉及读操作的成员方法,都要加const修饰,这样const对象也能调用,常对象也能调用。

Const方法只能读私有成员,不能修改。

总结

普通成员方法、const成员方法、static成员方法,差别主要是玩的是不同this指针,const玩的是const this指针,static干脆不玩指针。

指向类的成员的指针

可以在类的作用域下定义指针,指向public的类的成员方法或成员变量。

调用这个指针的时候需要依赖具体的对象。

#include <iostream>
class MyClass {
public:
    void memberFunction(int x) {
        std::cout << "Member function called with argument: " << x << std::endl;
    }
    int memberVariable = 42;
};
int main() {
    // 声明指向成员函数的指针
    void (MyClass::*functionPtr)(int) = &MyClass::memberFunction;
    // 声明指向成员变量的指针
    int MyClass::*variablePtr = &MyClass::memberVariable;
    MyClass obj;
    // 通过对象调用成员函数
    (obj.*functionPtr)(10);
    // 通过对象调用成员变量
    std::cout << "Member variable value: " << obj.*variablePtr << std::endl;
    return 0;
}
目录
相关文章
|
8天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
34 4
|
2月前
|
编译器 C++
C++ 类构造函数初始化列表
构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。
73 30
|
1月前
|
编译器 C语言 C++
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
19 1
|
1月前
|
C++
C++构造函数初始化类对象
C++构造函数初始化类对象
18 0
|
1月前
|
C++
C++入门4——类与对象3-2(构造函数的类型转换和友元详解)
C++入门4——类与对象3-2(构造函数的类型转换和友元详解)
21 0
|
3月前
|
编译器 C++
C++的基类和派生类构造函数
基类的成员函数可以被继承,可以通过派生类的对象访问,但这仅仅指的是普通的成员函数,类的构造函数不能被继承。构造函数不能被继承是有道理的,因为即使继承了,它的名字和派生类的名字也不一样,不能成为派生类的构造函数,当然更不能成为普通的成员函数。 在设计派生类时,对继承过来的成员变量的初始化工作也要由派生类的构造函数完成,但是大部分基类都有 private 属性的成员变量,它们在派生类中无法访问,更不能使用派生类的构造函数来初始化。 这种矛盾在C++继承中是普遍存在的,解决这个问题的思路是:在派生类的构造函数中调用基类的构造函数。 下面的例子展示了如何在派生类的构造函数中调用基类的构造函数:
|
4月前
|
存储 编译器 C语言
【C++基础 】类和对象(上)
【C++基础 】类和对象(上)
|
4月前
|
编译器 C++
【C++】详解构造函数
【C++】详解构造函数
|
9天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
32 4
|
1月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
27 4