前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【C++高潮——类与对象】:类的定义,访问限定符,类域,实例化,this指针,默认成员函数,构造,析构,拷贝构造,运算符重载,类型转换,static成员,友元,内部类,匿名对象,对象拷贝编译器优化

【C++高潮——类与对象】:类的定义,访问限定符,类域,实例化,this指针,默认成员函数,构造,析构,拷贝构造,运算符重载,类型转换,static成员,友元,内部类,匿名对象,对象拷贝编译器优化

作者头像
用户11029137
发布于 2025-03-11 00:19:40
发布于 2025-03-11 00:19:40
29100
代码可运行
举报
文章被收录于专栏:编程学习编程学习
运行总次数:0
代码可运行

一,类的基本认识

(一)类的定义

类是一种用户自定义的数据类型,它将数据(成员变量)和操作这些数据的函数(成员函数)封装在一起。在C++中,一般使用class关键字来定义类,其基本格式如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class 类名 {
public:
    // 成员函数声明
    void 函数名();
private:
    // 成员变量声明
    int 变量名;
};
  • 在上述代码中,class是定义类的关键字,类名是你为这个类取的名字,{}中为类的主体。类体中内容称为类的成员,类中的变量称为类的属性或成员变量,类中的函数称为类的方法或者成员函数。
  • 类定义结束时后面的**;**不能省略
  • 为了区分成员变量,一般习惯上会在成员变量前面或后面加特殊标识,如_ 或者以m开头,但这在C++中并不是强制的,主要是规范。
  • 除了class,C++中的struct也可以定义类。C++兼容C中struct的用法,并且对其进行了升级,struct中也可以定义函数。一般情况下推荐使用class来定义类。

下面是一个简单的类定义示例,以Stack类为例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Stack {
public:
    void Init(int n = 4) {
        array = (int*)malloc(sizeof(int) * n);
        if (nullptr == array) {
            perror("malloc申请空间失败");
            return;
        } // 成员函数
        capacity = n; // 成员变量
        top = 0;
    }
    void Push(int x) {
        // 扩容逻辑
        array[top++] = x;
    }
    int Top() {
        assert(top > 0);
        return array[top - 1];
    }
    void Destroy() {
        free(array);
        array = nullptr;
        top = capacity = 0;
    }
// public 到这都是公有的
private:
    int* array;
    size_t capacity;
    size_t top;
};

在这个Stack类中,包含了初始化栈(Init)、入栈(Push)、获取栈顶元素(Top)和销毁栈(Destroy)等成员函数,以及表示栈的数组(array)、容量(capacity)和栈顶位置(top)等成员变量。

(二)访问限定符

C++通过访问限定符来实现封装,将对象的属性与方法结合在一起,并通过访问权限选择性地将接口提供给外部用户使用。C++中有三种访问限定符:publicprotectedprivate

  • public修饰的成员在类外可以直接被访问;
  • protectedprivate修饰的成员在类外不能直接被访问
  • 使用class定义成员时,如果没有被访问限定符修饰,默认为private,而struct默认为public
  • 定义在类⾯的成员函数默认为inline

一般情况下,我们会将成员变量限制为privateprotected,以保护数据的安全性,而将需要给外部使用的成员函数设置为public

(三)类域

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用::作用域操作符指明成员属于哪个类域。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Stack {
public:
    void Init(int n);
private:
    int* array;
    size_t capacity;
    size_t top;
};
void Stack::Init(int n) {
    array = (int*)malloc(sizeof(int) * n);
    if (nullptr == array) {
        perror("malloc申请空间失败");
        return;
    }
    capacity = n;
    top = 0;
}

在上述代码中,Stack::Init表示Init函数属于Stack类域。如果不指定类域,编译器会把Init当成全局函数,那么编译时,在局部、全局找不到array等成员的声明/定义在哪⾥,就会编译错误。指定类域Stack,编译器就是知道Init是成员函数,当前域找不到的array等成员,就会到类域中去查找。


(四)实例化

4.1 实例化概念

用类类型在物理内存中创建对象的过程,称为类实例化出对象。类是对对象的一种抽象描述,它就像一个模型,限定了类有哪些成员变量,但这些成员变量只是声明,并没有分配空间(就像int类型)。只有用类实例化出对象时,才会为成员变量分配空间(就像int a;a是实例化)。一个类可以实例化出多个对象,这些对象占用实际的物理空间,用于存储类成员变量。(简单来说,类就是用户自定义的数据类型

下面是一个Date类的实例化示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Date {
public:
    void Init(int year, int month, int day) {
        _year = year;
        _month = month;
        _day = day;
    }
    void Print() {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};
int main() {
    Date d1;
    Date d2;
    d1.Init(2024, 3, 31);
    d1.Print();
    d2.Init(2024, 7, 5);
    d2.Print();
    return 0;
}

在上述代码中,Date类实例化出了d1d2两个对象,每个对象都有自己独立的成员变量,用于存储不同的日期数据。

4.2 对象大小

对象大小中包含成员变量,但不包含成员函数。因为成员函数被编译后是一段指令,存储在代码段中,对象中无法存储这些指令。

C++规定类实例化的对象也要符合内存对齐的规则(和C语言结构体内存对齐相同),如有不懂可看这篇文章C语言——结构体类型(二)【结构体内存对齐,结构体数组】

例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class A {
public:
    void Print() {
        cout << _ch << endl;
    }
private:
    char _ch;
    int _i;
};
int main() {
    A a;
    cout << sizeof(a) << endl;
    return 0;
}

在上述代码中,A类的对象a的大小,根据内存对齐规则计算,char类型占1字节,int类型占4字节,默认对齐数为8,所以_ch的对齐数为1,_i的对齐数为4,A类对象的总大小为8字节(因为总大小要满足成员最大对齐数4的整数倍)。


(五)this指针

我们都知道,因为类里面的成员函数的实现是固定的,当不同对象调用类里面同一个成员方法的时候,成员函数是如何知道应该访问哪个对象的成员变量呢(如初始化,即给哪一个对象的成员变量赋值)?这就要靠C++提供的隐含的**this**指针

编译器编译后,类的成员函数默认都会在形参第一个位置增加一个当前类类型的指针,叫做this指针。例如,Date**类的**Init**函数的真实原型为*`void Init(Date const this, int year, int month, int day)`

在类的成员函数中访问成员变量,本质都是通过this指针访问的。例如,Init函数中给_year赋值,实际上是this->_year = year 。虽然C++规定不能在实参和形参的位置显示地写**this**指针,但可以在函数体内,或者函数返回显示使用this指针。

下面通过两个示例来理解this指针的作用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Date {
public:
    void Init(int year, int month, int day) {
        this->_year = year;
        this->_month = month;
        this->_day = day;
    }
    void Print() {
        cout << this->_year << "/" << this->_month << "/" << this->_day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};
int main() {
    Date d1;
    Date d2;
    d1.Init(2024, 3, 31);
    d1.Print();
    d2.Init(2024, 7, 5);
    d2.Print();
    return 0;
}

在上述代码中,d1d2调用InitPrint函数时,this指针会分别指向d1d2,从而确保成员函数能够正确访问相应对象的成员变量。


二,类的成员函数

(一)类的默认成员函数

隐式成员函数:当用户没有显式实现时,编译器自动生成的成员函数

一个类,我们不写的情况下,编译器会默认生成以下6个成员函数(常见的,前4比较重要)

在学习成员函数时,我们应该关注:

  1. 默认生成的成员函数的行为是什么,能否满足需求
  2. 如果不能满足需求,我们如何实现(即:要清楚这些函数的实现要求)

(二)构造函数

构造函数:用于在对象实例化时对其进行初始化。

构造函数的特点:

  • 函数名与类名相同
  • 没有返回值,连void也不需要写
  • 对象实例化时,系统会自动调用对应的构造函数
  • 支持重载,我们可以根据不同的需求定义多个构造函数。
  • 如果类中没有显式定义构造函数,编译器会生成一个无参的隐式构造函数;但一旦用户自己定义了构造函数,编译器就不再生成。
  • 无参构造函数全缺省构造函数以及编译器默认生成的隐式构造函数,都属于默认构造函数。不过,这三种函数存在时只能存在一个,若同时存在,调用时会产生歧义。
  • 编译器默认生成的隐式构造函数,对于内置类型的成员变量是否初始化是不确定的,这取决于编译器.
  • 当一个类包含自定义类型的成员变量,并且在该类的构造函数中没有显式初始化这些自定义成员变量时,编译器会在创建该类对象时自动调用这些成员变量所属类型的默认构造函数。如果自定义类型没有默认构造函数,就会报错。

下面是Date类的构造函数示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class stu_class {
public:
	// 1,无参数构造函数
	stu_class() {
		_grade = 3;
		_clas = 5;
	}
	// 2,全缺省构造函数
	stu_class(int grade = 3, int clas = 5) {
		_grade = grade; 
		_clas = clas;
	}
	// 3,带参数构造函数
	stu_class(int grade, int clas) 
	{
		_grade = grade;
		_clas = clas;
	}
private:
	int _grade;
	int _clas;
};

注意:这里的 1 和 2 ,只能同时存在一个。2 和 3 也不能同时存在,因为不构成函数重构。

创建对象并调用默认构造函数的写法:

1,调用不带参数的:stu_class stu1; ,注意不能写成stu_class stu1();,因为会和函数声明歧义

2,调用带参数的:stu_class stu2(6, 3);

构造函数使用实例:

示例一:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
using namespace std;
class stu_class {
public:
	void stu_print() {
		cout << "grade " << _grade << " " << "class " << _clas << endl;
	}
	// 1,无参数构造函数
	stu_class() {
		_grade = 3;
		_clas = 5;
	}
	// 3,带参数构造函数
	stu_class(int grade, int clas) 
	{
		_grade = grade;
		_clas = clas;
	}
private:
	int _grade;
	int _clas;
};
int main() {
	stu_class stu1;
	stu_class stu2(6,3);
	stu1.stu_print();
	stu2.stu_print();
	return 0;
}

运行结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
grade 3 class 5
grade 6 class 3

示例二:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
using namespace std;
class stu_class {
public:
	void stu_print() {
		cout << "grade " << _grade << " " << "class " << _clas << endl;
	}
	// 1,全缺省构造函数
	stu_class(int grade = 5, int clas = 3) {
		_grade = grade;
		_clas = clas;
	}

private:
	int _grade;
	int _clas;
};
int main() {
	stu_class stu1; // 调用全缺省
	stu_class stu2(6,3); // 给全缺省传参
	stu1.stu_print();
	stu2.stu_print();
	return 0;
}

运行结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
grade 5 class 3
grade 6 class 3

示例三:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
using namespace std;

class stu_class {
public:
	void stu_print() {
		cout << "grade " << _grade << " " << "class " << _clas << endl;
	}
	stu_class(int grade = 5, int clas = 3) {
		_grade = grade;
		_clas = clas;
	}

private:
	int _grade;
	int _clas;
};

class two_stu {
public:
	void print() {
		stu1.stu_print();
		stu2.stu_print();
		cout << "neizhi: " << _neizhi << endl;
	}
	two_stu(int neizhi = 1) {
		_neizhi = neizhi;
	}

private:
	int _neizhi; // 内置变量
	stu_class stu1;
	stu_class stu2;
};

int main() {
	two_stu two;  
	two.print();
}

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
grade 5 class 3
grade 5 class 3
neizhi: 1

解释一下代码:

  • 首先创建了stu_class类,成员变量:年级、班级,以及自定义了构造函数(且这个函数是一个默认构造函数);
  • 再创建了two_stu类,成员变量:两个stu_class类型变量,一个内置类型变量,自定义构造函数函数中没有显式初始化这些自定义成员变量。
  • 然后我们创建two_stu 的实例观察two_stu的构造函数的使用情况:发现:对于自定义变量**stu_class**,调用了它自身的默认构造函数

在包含自定义类型变量的类中,直接不提供构造函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
using namespace std;

class stu_class {
public:
	void stu_print() {
		cout << "grade " << _grade << " " << "class " << _clas << endl;
	}
	stu_class(int grade = 5, int clas = 3) {
		_grade = grade;
		_clas = clas;
	}

private:
	int _grade;
	int _clas;
};

class two_stu {
public:
	void print() {
		stu1.stu_print();
		stu2.stu_print();
		cout << "neizhi: " << _neizhi << endl;
	}

private:
	int _neizhi; // 内置变量
	stu_class stu1;
	stu_class stu2;
};

int main() {
	two_stu two;  
	two.print();
}

运行结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
grade 5 class 3
grade 5 class 3
neizhi: -858993460
2.1 初始化列表

初始化列表可以理解为是对构造函数的一个改进。即:除了在构造函数体内进行赋值初始化外,还可以使用初始化列表来初始化数据成员。初始化列表存在于每个构造函数中,且是每个成员变量定义初始化的地方(即:每个成员都需要经过初始化列表)

2.1.1 语法

下面是一个简单的初始化列表:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Date {
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	// 上面这里是初始化列表,以 : 开头,用 , 分割变量
	// ()内是传入的用于初始化成员变量的参数
	{
		// 这是函数体
	}

private:
	int _year;
	int _month;
	int _day;
};

注意:

  • 每个成员变量在初始化列表中只能出现⼀次
  • 引⽤成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进⾏初始化,否则会编译报错
  • 初始化列表中按照成员变量在类中声明顺序进⾏初始化
  • C++11⽀持在成员变量声明的位置给缺省值,当变量没有在初始化化列显式的初始化时,会用这个缺省值初始化成员变量
  • 如果即没有缺省值,又没有在初始化列表显式的初始化,则对于内置类型,是否初始化行为是不确定的,对于自定义类型,有默认构造则调用默认构造,没有则报错
2.1.2 示例

用上面的Date类举例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
using namespace std;
class Date {
public :
	Date(int year = 1900, int month = 9, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{}

	void Print() {
		cout << _year << "/" << _month << "/" << _day << endl;
	}
//这里是声明:
private:
	int _year = 1945;
	int _month = 9;
	int _day = 2;
};

int main() {
	Date d1;
	d1.Print(); 
	Date d2(2025, 9, 3);
	d2.Print();
	return 0;
}

输出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1900/9/1
2025/9/3

对于,首先,对于d1d2,都没有我们用到声明时给的缺省值,因为这3个成员变量都在初始化列表中有显式的初始化。对于d1,应为没有传参,所以初始化列表用了参数列表(int year = 1900, int month = 9, int day = 1)里面的缺省值对变量进行了初始化,对于d2用了传入的参数进行初始化。

使用声明时缺省参数的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
using namespace std;
class Date {
public :
	Date(int year, int month, int day = 1)
		:_month(month)
		,_day(day)
	{}

	void Print() {
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year = 1945;
	int _month = 9;
	int _day = 2;
};

int main() {
	Date d1(2000, 9, 10);
	d1.Print(); 
	Date d2(2025, 10);
	d2.Print();
	return 0;
}

在这里,不管第一个参数传什么给year都没用,因为初始化列表里面没有用到year参数来初始化_year,所以都是使用声明时给的缺省值来初始化_year

const成员变量、引用和类类型的初始化:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Time
{
public:
	Time(int x = 1)
	{
		_t = x;
	}
private:
	int _t;
};

class Date {
public :
	Date(int &x, int y)
		:_ref(x)  // 引用初始化,将 _ref 绑定到传入的参数 x,绑定后不能再修改
		,_t(3)   // 类类型初始化,调用 Time 类的构造函数,传入参数 y 来初始化对象 _t
		,_y(y)  // 初始化 const 类型的变量 _y,必须在初始化列表中完成,之后不能修改
	{}

private:
	int& _ref;
	Time _t;
	const int _y;
};

(三)析构函数

析构函数:用于在对象销毁时执行清理操作(如函数调用结束后,局部变量会自动销毁)。主要作用是释放对象占用的资源(如动态内存、文件句柄、网络连接等),确保程序不会发生资源泄漏。

析构函数的特点如下:

  • 函数名是在类名前加上字符~
  • 无参数且无返回值。
  • 一个类只能有一个析构函数,如果没有显式定义,系统会自动生成默认的析构函数。
  • 对象生命周期结束时,系统会自动调用析构函数
  • 编译器自动生成的析构函数对内置类型成员不做处理,但会调用自定义类型成员的析构函数。而且,即使我们自己显式编写了析构函数,自定义类型成员的析构函数依然会被调用。
  • 如果类中没有申请资源,析构函数可以不写,直接使用编译器生成的默认析构函数即可;但如果类中申请了资源,就必须自己编写析构函数,否则会导致资源泄漏。
  • 在一个局部域中,多个对象的析构顺序是后定义的先析构(后定义的在栈顶,先销毁)。

下面是Stack类的析构函数示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:
    Stack(int n = 4){
        _a = (STDataType*)malloc(sizeof(STDataType) * n);
        if (nullptr == _a){
            perror("malloc申请空间失败");
            return;
        }
        _capacity = n; _top = 0;
    }
    ~Stack() {
        cout << "~Stack()" << endl;
        free(_a);
        _a = nullptr;
        _top = _capacity = 0;
    }
private:
    STDataType* _a;
    size_t _capacity;
    size_t _top;
};
int main() {
    Stack s1;
    return 0;
}

这里推荐手动调试一下,观察_a地址的变化:

下面给出调试到malloc语句分配好内存以及刚到return语句,自动进入析构函数内部的调试截图:

1,执行到Stack内部,刚执行完malloc,注意看_a地址

2,按F11单步执行,一直到return 0s1生命周期要结束,再按一下F11,自动调用析构函数~Stack(),进入~Stack()

(四)拷贝构造函数

4.1 拷贝

我们先理解拷贝这个概念。

在C++中,传值传参传值返回时,都会发生拷贝。传值传参和传值返回的本质是创建一个新的对象或值,并将其传递给调用者,这个过程就叫做拷贝,是不可避免的,除非编译器进行了优化。可以简单理解为:

  • 没有优化时,返回值先复制给临时变量,临时变量再复制给接收对象,两次拷贝
  • 优化后:不使用临时变量,直接将返回值拷贝给接收对象

内置类型和自定义类型的拷贝行为不同:

  • 内置类型:直接复制值,过程就是简单的内存拷贝,一个字节一个字节的复制,得到新副本
  • 自定义类型:会调用拷贝构造函数,得到新副本
4.2 拷贝构造函数

拷贝构造函数是一种特殊的构造函数,用于使用同类对象初始化创建新对象。如果一个构造函数的第一个参数是自身类类型的引用,且任何额外的参数都有默认值,那么它就是拷贝构造函数。

拷贝构造函数有以下特点:

  • 它是构造函数的一个重载形式。
  • 第一个参数必须是类类型对象的引用,后面的参数要有缺省值。第一个参数使用传值方式会导致编译器报错(引发无穷递归调用)。
  • 如果没有显式定义拷贝构造函数,编译器会自动生成。自动生成的拷贝构造函数对内置类型成员变量进行浅拷贝(浅拷贝,即一个字节一个字节地拷贝),对自定义类型成员变量会调用其拷贝构造函数
  • 自定义类型中:如果变量全是内置类型,并且无资源分配,则编译器自动生成的拷贝构造函数(对内置变量进行浅拷贝)就能满足需求
  • Stack类,虽然成员变量也是内置类型,但其中的_a指针指向了资源,编译器自动生成的浅拷贝就不符合要求,需要我们自己实现深拷贝(对指向的资源也进行拷贝)
  • 通常,如果一个类显式实现了析构函数并释放资源,那么通常也需要显式编写拷贝构造函数。
  • 传值返回会产生一个临时对象并调用拷贝构造函数,而传值引用返回返回的是对象的别名,不会产生拷贝。(但是,如果返回对象是当前函数局部域的局部对象,则返回对象在函数结束后会被销毁,此时引用返回会产生野引用,所以只有当返回对象在函数结束后依然存在时,才能使用引用返回。)

下面是Date类和Stack类的拷贝构造函数示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
using namespace std;
class Date
{
public:
    Date(int year = 1, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    Date(const Date& d) // 第一个参数必须是类类型对象的引用
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};

class Stack // 假设数据都是整型
{
public:
    Stack(int n = 4)
    {
        _a = (int *)malloc(sizeof(int) * n);
        if (nullptr == _a)
        {
            perror("malloc申请空间失败");
            return;
        }
        _capacity = n;
        _top = 0;
    }
    Stack(const Stack& st) // 拷贝构造函数
    {
        _a = (int *)malloc(sizeof(int) * st._capacity); // 深拷贝
        if (nullptr == _a)
        {
            perror("malloc申请空间失败!!!");
            return;
        }
        memcpy(_a, st._a, sizeof(int) * (st._top+1));
        _top = st._top;
        _capacity = st._capacity;
    }
    ~Stack()
    {
        cout << "~Stack()" << endl;
        free(_a);
        _a = nullptr;
        _top = _capacity = 0;
    }
    void Print() {
        cout << _capacity << endl;
    }
private:
    int* _a;
    size_t _capacity;
    size_t _top;
};

int main() {
    Date d1(2025, 3, 8); // 构造函数初始化
    Date d2(d1);  // 拷贝构造函数初始化
    Date d3 = d2; // 这也是拷贝构造的写法
    Stack s1;
    Stack s2 = s1;
    d3.Print();
    cout << "s2的capacity: ";
    s2.Print();
    return 0;
}

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
2025-3-8
s2的capacity: 4
~Stack()
~Stack()

(五)const成员函数

const成员函数是指在函数声明和定义后加上const关键字的成员函数。const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进⾏修改

如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 假如这个Print 是定义在Date类里面的
void Print() const
{
cout << _year << "-" << _month << "-" << _day << endl;
}

在没有constvoid Print()Print() 参数列表隐藏的参数:Date* const this (注意这个const修饰的指针,表示指针本身内容不可以修改,但是指针指向空间可以改)

加了const以后,参数变成:const Date* const this代表指针指向内容不能修改,即:类的任何成员不可修改。

同一个函数名可以有const和非const两个版本(即:函数重载),编译器会根据调用对象的const性质来选择调用哪个版本。

示例:

三,运算符重载

运算符重载就是:允许我们为自定义类型(类或结构体)定义运算符的行为。通过运算符重载,可以使自定义类型的对象像内置类型一样使用运算符。

运算符重载语法:

  • 运算符重载是有特殊名字的函数,有返回类型,参数列表和函数体。名字格式:**operator<运算符>**,这个运算符要是语法中有的。
  • 重载运算符函数的参数个数和该运算符作⽤的运算对象数量⼀样多。即一元运算符就只能有一个参数,二元就两个,第一个参数对应左运算对象,第二个对应右运算对象。且至少有一个自定义类型的参数。
  • 如果⼀个重载运算符函数是成员函数,则它的第⼀个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数⽐运算对象少⼀个。
  • .*::.? :sizeof,这五个运算符不能重载
  • C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,⽅便区分。
  • 重载<<>>时,需要重载为全局函数,并把ostream/istreamcoutcin 的类)放到第⼀个形参位置,第二个参数放自定义类型。因为重载为成员函数时,this指针默认抢占了第⼀个形参位置,第⼀个形参位置是左侧运算对象,调⽤时就变成了 对象<<cout,不符合使⽤习惯和可读性。
示例一
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
using namespace std;

class Date {
public:
	Date(int year, int month, int day) {
		_year = year;
		_month = month;
		_day = day;
	}
	// 定义一个运算符重载(类的成员函数可以访问私有变量)
	// 的访问控制权限是基于类本身,而非类的具体对象,所以可以访问d2
	bool operator==(Date d2){
		return _year == d2._year && _month == d2._month && _day == d2._day;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main() {
	Date d1(2025, 3, 8);
	Date d2(2025, 3, 9);
	cout << d1.operator==(d2) << endl;  // 输出 0
	return 0;
}
示例二

前置++和后置++区别:

前置++:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Dater& operator++()

后置++

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Dater& operator++(int) // 在参数列表多加一个 int
示例三

重构流插入<<和流提取>>

如:cin >> x >> y ;:从左往右结合,返回左操作数(即返回 cin 本身)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
using namespace std;

class Date {
	// 友元函数,文章后面会讲,先理解为,让这个函数可以使用private对象
	friend istream& operator>>(istream& in, Date& d); 
	friend ostream& operator<<(ostream& out, const Date& d);
public:
	Date(int year = 2025, int month = 3, int day = 1) {
		_year = year;
		_month = month;
		_day = day;
	}

private:
	int _year;
	int _month;
	int _day;
};
// 在全局重载 << 
ostream& operator<<(ostream& out, const Date&d) { // 不能修改引用对象内容
	out << d._year << "/" << d._month << "/" << d._day << endl;
	return out;
}


// 重载 >> 
istream& operator>>(istream& in, Date& d) { // 这里要插入数据,所以不能加const
	cout << "请依次输入年月日: " << endl; 
	cin >> d._year >> d._month >> d._day; 
	return in; // 返回istream类型的引用,可实现连续赋值
}

int main() {
	Date d1;
	cin >> d1;
	cout << d1;
}

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
请依次输入年月日:
2025 3 9
2025/3/9
6.1 赋值运算符重载

赋值运算符重载用于完成两个已经存在的对象之间的拷贝赋值,这和拷贝构造函数不同,拷贝构造函数是用一个对象拷贝初始化另一个正在创建的对象。

赋值运算符重载的特点如下:

  • 它是一个运算符重载,并且规定必须重载为成员函数。参数建议写成**const**当前类类型引用,这样可以避免传值传参带来的拷贝。
  • 有返回值,且建议返回当前类类型引用,引用返回可以提高效率,返回值的目的是为了支持连续赋值的场景。
  • 如果没有显式实现,编译器会自动生成一个默认的赋值运算符重载。其行为和默认拷贝构造函数类似,对内置类型成员变量进行值拷贝(浅拷贝),对自定义类型成员变量会调用其赋值运算符重载函数。
  • 和拷贝构造函数类似,像Date类这种成员变量全是内置类型且没有指向特殊资源的类,编译器自动生成的赋值运算符重载就能满足需求;而像**Stack**类这种有指针指向资源的类,就需要我们自己实现深拷贝

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
using namespace std;
// 赋值运算符重载
class Stack // 假设数据都是整型
{
public:
    // 构造函数
    Stack(int capacity = 4)
    {
        _a = (int*)malloc(sizeof(int) * capacity);
        if (_a == nullptr)
        {
            perror("内存空间分配失败");
            return;
        }
        _capacity = capacity;
        _top = -1;
    }
    // 拷贝构造函数
    Stack(const Stack& st)
    {
        _a = (int*)malloc(sizeof(int) * st._capacity);
        if (_a == nullptr)
        {
            perror("内存空间分配失败");
            return;
        }
        memcpy(_a, st._a, sizeof(int) * (st._top + 1)); // 按字节一个个复制
        _capacity = st._capacity;
        _top = st._top;
    }
    // 赋值运算符重载
    Stack& operator=(const Stack& st)
    {
        free(_a); // 需要先释放之前的
        _a = (int*)malloc(sizeof(int) * st._capacity);
        if (_a == nullptr)
        {
            perror("内存空间分配失败");
            return *this;
        }
        memcpy(_a, st._a, sizeof(int) * (st._top + 1)); // 按字节一个个复制
        _capacity = st._capacity;
        _top = st._top;
        return *this; // 返回引用,支持连续赋值
    }
    ~Stack() {
        free(_a);
        _a = nullptr;
        _capacity = 0;
        _top = -1;
    }
    void Print()
    {
        cout << _capacity << endl;
    }
private:
    int* _a;
    size_t _capacity;
    size_t _top;
};
int main() {
    Stack st1(8);
    Stack st2;
    cout << "运用赋值重载之前,st2 capacity: ";
    st2.Print();
    Stack st3;
    st3 = st2 = st1;  // 赋值运算的结合率是从右往左,返回值是左操作数
    cout << "运用赋值重载之后,st2 capacity: ";
    st2.Print();
    cout << "st3 capacity: ";
    st3.Print();
    return 0;
}

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
运用赋值重载之前,st2 capacity: 4
运用赋值重载之后,st2 capacity: 8
st3 capacity: 8

6.2 取地址运算符重载

取地址运算符重载有两种形式:普通取地址运算符重载 和 const取地址运算符重载(const取出的不能改变所指空间内容)。编译器会为类默认生成取地址运算符的重载版本,无需自行实现。

使用示例(使用Date类):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main() {
	Date d1;
	Date* d1_ptr = &d1;
	const Date* c_d1_ptr = &d1;
	printf("%p\n", d1_ptr);
	printf("%p", c_d1_ptr);
}

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
00000023F82FF5A8
00000023F82FF5A8

四,类型转换

C++支持内置类型隐式转换为类类型对象,前提是有相关内置类型为参数的构造函数(参数数量不限)。比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class A {
public:
    A(int a1) : _a1(a1) {}
    A(int a1, int a2)
        :_a1(a1)
        , _a2(a2)
    {}
private:
    int _a1 = 1;
    int _a2 = 2;
};
int main() {
    A aa1 = 1;
    A aa2 = { 2,2 };
    return 0;
}
  • 在这段代码中,int类型的1被隐式转换为A类对象aa1。实现过程:先调用A(int a1) 这个单参数构造函数构造⼀个A的临时对象,临时对象的类型是 A 类,再⽤这个临时对象拷⻉构造aa3,(有些编译器自行优化:遇到连续构造+拷⻉构造->一次构造,不产生临时对象)
  • { 2,2 }多参数转换成A类对象aa2(C++11 后支持的)
  • 如果不想支持这种隐式转换,可以在构造函数前加explicit关键字。
  • 类类型的对象之间也能隐式转换,同样需要相应构造函数支持。

五,static成员

static修饰的成员变量叫静态成员变量,它为所有类对象共享,不属于某个具体对象,存放在静态区,并且一定要在类外初始化。静态成员也受访问限定符影响。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class A {
public:
    A() { ++_scount; }
    static int GetACount() { return _scount; }
private:
    static int _scount; 
};
int A::_scount = 0; 
int main() {
    cout << A::GetACount() << endl; 
    A a1, a2;
    cout << A::GetACount() << endl; 
    return 0;
}

这里_scount是静态成员变量,用于统计A类对象的个数。通过A::GetACount()可以访问这个静态成员变量。

static修饰的成员函数叫静态成员函数,它没有this指针。

  • 静态成员函数只能访问其他静态成员,没有this指针无法访问其他成员。
  • 非静态成员函数可以访问任意静态成员变量和静态成员函数。
  • 突破类域就可以访问静态成员/函数,可以通过类名::静态成员或者对象.静态成员的方式。
  • 静态成员变量不能在声明位置给缺省值初始化,因为静态成员不属于某个对象,不经过构造函数初始化列表

六,友元

友元提供了突破类访问限定符封装的方式,分为友元函数和友元类。友元类中的成员函数都是另一个类的友元函数。实现:在函数声明或类声明前加friend,并把友元声明放在类里。

友元函数可以访问类的私有和保护成员,它不是类的成员函数,可在类定义的任何地方声明,不受访问限定符的限制,且一个函数可以是多个类的友元函数。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class A;
class B;
class A {
    friend void func(const A& aa, const B& bb);  // 友元声明,这样就可以访问A类的私有和保护成员
private:
    int _a1 = 1;
};
class B {
    friend void func(const A& aa, const B& bb);
private:
    int _b1 = 3;
};
void func(const A& aa, const B& bb) {
    cout << aa._a1 << endl;
    cout << bb._b1 << endl;
}

在这个例子中,func函数是AB类的友元函数,能够访问它们的私有成员。

友元类的关系是单向的且不能传递。比如A类是B类的友元,B类不一定是A类的友元;AB的友元,BC的友元,但A不是C的友元。

七,内部类

如果一个类定义在另一个类内部,就是内部类。

  • 内部类是独立的类,受外部类类域和访问限定符限制,外部类对象不包含内部类
  • 内部类默认是外部类的友元类
  • 内部类本质也是⼀种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使⽤,那么可以考虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其他地⽅都⽤不了。

例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class A {
private:
    static int _k;
    int _h = 1;
public:
    class B { 
    public:
        void foo(const A& a) {
            cout << _k << endl; 
            cout << a._h << endl; 
        }
    };
};
int A::_k = 1;
int main() {
    A::B b;
    A aa;
    b.foo(aa);
    return 0;
}

在这段代码中,B的实现主要是输出A的私有变量,BA的内部类,B的成员函数foo可以访问A的私有成员。

八,匿名对象

类型(实参)定义出来的对象就是匿名对象(平时的叫有名对象),它的生命周期只在当前一行。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class A {
public:
    A(int a = 0) : _a(a) { cout << "A(int a)" << endl; }
    ~A() { cout << "~A()" << endl; }
private:
    int _a;
};
int main() {
    A(1); 
    return 0;
}

这里A(1)就是一个匿名对象,在这行代码执行完后,它就会被析构。匿名对象适用于临时使用一下对象的场景。

九,对象拷贝时的编译器优化

本文(四)拷贝构造的时候,提到了拷贝,现在再探讨对象拷贝时编译器的优化。在 C++ 中,对象拷贝时的编译器优化是提升程序性能的关键机制。如何优化C++标准并没有严格规定,各个编译器会根据情况⾃⾏处理。

Visual Studio 2022编译器的优化举例(2022看不到优化之前的):

可以用2019调试,关闭优化步骤: 项目属性(右击项目) → C/C++ → 优化 → 已禁用 (/Od)

当遇到连续拷贝+拷贝构造时,编译器会直接优化成一次构造

下面,我们通过打印语句来观察编译器的优化:

代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
using namespace std;

class A
{
public:
	A(int a = 0)
		:_a1(a)
	{
		cout << "A(int a)" << endl;
	}
	A(const A& aa)
		:_a1(aa._a1)
	{
		cout << "A(const A& aa)" << endl;
	}
	A& operator=(const A& aa)
	{
		cout << "A& operator=(const A& aa)" << endl;
		if (this != &aa)
		{
			_a1 = aa._a1;
		}
		return *this;
	}

	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a1 = 1;
};
void f1(A aa)
{
}
A f2()
{
	A aa;
	return aa;
}

int main()
{

	 隐式类型,连续构造+拷⻉构造->优化为直接构造
	A aa1 = 1;
	cout << endl;
	return 0;
}

未优化( 1 次构造 + 1 次拷贝构造,伴随 2 次析构。):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
A(int a)          // 1. 用 1 构造临时对象
A(const A& aa)    // 2. 用临时对象拷贝构造 aa1
~A()              // 3. 临时对象析构
                  // (此处输出换行)
~A()              // 4. main 结束时 aa1 析构

优化后(1 次构造):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
A(int a) // 只有一次构造,直接构造出aa1

~A()  // 这是打印endl之后的语句,所以是对aa1的析构

优化后:编译器会把 A aa1 = 1; 当作 A a11(1); 来处理。也就是说,aa1 是直接使用 1 作为参数通过构造函数 A(int a) 构造出来的,没有中间的临时对象产生。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-03-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
宝塔面板7.9.0已发布(Linux)-星泽V社
【重构】重构面板设置页面 【调整】调整部分UI显示效果 【调整】不再提供入口关闭功能 【调整】phpMyAdmin使用面板端口访问,不再依赖888端口 【调整】面板SSL使用宝塔自签证书 【增加】增加未认证响应状态配置选项(在面板设置-安全设置) 【增加】增加nginx配置文件关键配置检测机制 【增加】网站、FTP、数据库容量配额功能(企业版专享) 【增加】软件列表增加预览功能 【增加】增加远程数据库服务器管理的支持 【增加】面板配置页面增加面板请求相关配置 【增加】增加HTTPS防窜站功能(在网站-安全设置) 【增加】增加面板HTTP代理模块,可通过/p/端口号/访问本地HTTP端口 【优化】优化面板SSL模块 【优化】Let’sEncrypt自动续签时同时续签订单之外的同品牌证书 【优化】优化每天首次登录速度 【修复】修复面板密码无法使用某些特殊符号的问题 【修复】Node项目/Java项目在Apache环境下开启SSL无效的问题 【修复】修复CURL方式请求的问题 【修复】修复部分机器备份数据库失败的问题 【修复】修复PHP命令行 alias未修改的问题 【修复】其它已知Bug修正
星泽V社
2022/03/16
8830
宝塔面板7.9.0已发布(Linux)-星泽V社
宝塔面板7.4.2及Windows面板6.8数据库鉴权漏洞 – 官方发布紧急安全更新
据了解,此次更新是为了修复phpmyadmin未鉴权,可通过特定地址直接登陆数据库的严重Bug。存在安全漏洞的面板据悉为linux面板7.4.2版本,Windows面板6.8版本。(就是宝塔的程序员缓存了phpmyadmin的密码  /pma 未授权访问,前提是你在此之前需要从宝塔面板自动登录过phpmyadmin!!!)
陌涛
2020/08/25
1.6K0
宝塔面板7.4.2及Windows面板6.8数据库鉴权漏洞 – 官方发布紧急安全更新
宝塔历史版本离线下载(附4种安装教程)
这么做的目的是防止不小心点击了宝塔的版本更新,避免好不容易恢复的代码又自动升级了。 宝塔系统代码默认安装完是在/www/server/panel,接着我们直接将将压缩包内的panel目录上传到这里来,覆盖掉已有的文件。重启下宝塔,即可发现系统版本号已经恢复成7.4.2了
超级小可爱
2023/02/20
8.2K0
宝塔面板回滚7.4.5版本,可绕过宝塔强制登录、目录保护等。
宝塔面板目前稳定版的最新版本是7.6.0,测试版本目前的最新版本是7.8.10,需要说明的是,目前安装好宝塔的最新版之后,强制绑定账号是必选项,虽然说有技巧可以绕过,但是随着时间推移,也可能没那么容易绕过。
会长君
2023/04/25
2.2K0
宝塔面板回滚7.4.5版本,可绕过宝塔强制登录、目录保护等。
宝塔面板漏洞可直接登录sql数据库,重大安全事故
其他人,哪怕没有登录过面板和数据库的,就能直接通过 IP:888/pma 直接登陆你的数据库
wo.
2021/06/15
6.1K0
宝塔面板漏洞可直接登录sql数据库,重大安全事故
WordPress建站:使用宝塔Linux面板,忘记宝塔面板密码的解决方案
越来越多的人使用宝塔Linux面板来管理我们的服务器网站,最主要他是可视化的操作方式更加符合我们的一般用户习惯,同时对于没有技术知识的人来说就算是解决了大大的问题了。
wordpress建站吧
2019/07/17
1.5K0
WordPress建站:使用宝塔Linux面板,忘记宝塔面板密码的解决方案
安全通告|宝塔面板数据库管理未授权访问漏洞风险通告
近日,腾讯云安全运营中心监测到,宝塔面板官方发布通告,披露了一个数据库管理未授权访问漏洞,漏洞被利用可导致数据库管理页面未授权访问。 为避免您的业务受影响,腾讯云安全建议您及时开展安全自查,如在受影响范围,请您及时进行更新修复,避免被外部攻击者入侵。 漏洞详情 宝塔面板存在未授权访问漏洞,利用该漏洞,攻击者可以通过访问特定URL,直接访问到数据库管理页面,从而达到访问数据库数据、获取系统权限、进行危险操作等目的。 风险等级 高风险 漏洞风险 攻击者可利用该漏洞访问特定URL,从而直接访问到数据库管理
云鼎实验室
2020/08/25
1.1K0
宝塔面板降级到任意版本命令教程
如何降级安装,这里列举从9.4.0版本降级到9.3.0,如果需要其他版本,自己更改版本号即可。脚本如下:
是山河呀
2025/01/22
1510
bt5.9手动开心
还有一种方法(方法2),先正常安装5.9.2专业版,然后用5.9.0的common.py替换5.9.2的,再按照网上流传的修改方法(164行修改为[data = {'status' : True,'msg' : {'endtime' : 32503651199 }};])
Erwin
2020/09/25
6010
bt5.9手动开心
宝塔面板 linux命令大全
(请根据安装PHP版本号做更改,例如:/etc/init.d/php-fpm-54 start)
用户1700948
2018/10/10
4.5K0
宝塔BT面板无法启动修复方法
wget -O update.sh http://download.bt.cn/install/update.sh && sh update.sh
用户9042463
2021/09/29
7.3K0
用免费腾讯云服务器体验基于CentOS搭建宝塔面板环境
首次可免费使用云主机 30 分钟 ,到期后云主机将被重置并退库,若想保留成果请及时留用。
用户5915102
2019/09/07
8.6K0
用免费腾讯云服务器体验基于CentOS搭建宝塔面板环境
Linux VPS安装5.9宝塔面板并免费升级到专业版
5.9版本的宝塔面板相比最新版更加适用,没有过多臃肿的依赖,而且需要使用的功能基本都有; 5.9版本对于内存占用小,纯面板大约50M左右,对小内存的机器就更加合适了; 5.9版本因为官方已经停止更新,所以使用专业版基本无风险;
骤雨重山
2022/01/17
4.2K0
Linux VPS安装5.9宝塔面板并免费升级到专业版
宝塔面板安装使用常见问题收集
使用 SSH 连接工具,如 堡塔SSH终端连接到您的 Linux 服务器后, 挂载磁盘,根据系统执行相应命令开始安装(大约2分钟完成面板安装):
用户7146828
2021/08/09
2.6K0
Docker安装/卸载宝塔面板
添加docker用户权限 sudo usermod -aG ${USER} sudo gpasswd -a ${USER} docker sudo systemctl restart docker newgrp docker 拉取centos镜像 docker pull centos 列出所有容器 docker ps -a 创建baota容器 docker run -it -d --name baota -p 9090:8888 -p 9091:80 -p 9092:81 -p 9093:82 -v /ho
小锋学长生活大爆炸
2021/06/11
1.1K0
宝塔面板安装使用
手工输入命令安装各类软件,操作起来费时费力并且容易出错,而且需要记住很多Linux的命令,非常复杂。本文记录使用宝塔面板配置系统环境的流程。 宝塔简介 配置系统环境总是很繁琐,尤其是配置自己并不熟悉的环境,容易出错而且费神。 宝塔面板是一款永久免费的安全高效的服务器运维面板,有以下优势: 服务器运维面板:2分钟装好面板,一键管理服务器,鼠标点几下就能替代以前的复杂繁多命令,操作简单,看一眼就会使用。 快速创建管理web项目:方便便捷的网站管理功能,例如域名绑定,一键部署SSL证书,更改网站配置等
为为为什么
2022/08/09
9880
宝塔面板安装使用
Docker 安装配置宝塔面板
本文主要介绍如何将宝塔面板作为 Docker 容器安装在 Linux 服务器上。其实对于宝塔一开始我是拒绝的,毕竟我之前是可以熟练操作 Linux 服务器的。别提宝塔面板了,就连 lnmp.org 一键安装包我都不用,全程命令行操作无压力。这一切都是从入行前端之后改变了。每次想搞点什么的时候,都要先熟悉一遍命令很耽误事,这时我就想起宝塔来了。Docker 的入门,可以看下我的垃圾文,保证前端也能
用户1250838
2021/05/31
18.7K0
Docker 安装配置宝塔面板
宝塔Linux面板常用命令整理汇总
宝塔面板,一款使用方便、功能强大且终身免费的服务器管理软件,支持Linux与Windows系统。一键配置:LAMP/LNMP、网站、数据库、FTP、SSL,通过Web端轻松管理服务器。
主机教程网2bcd.com
2022/09/18
1.3K0
宝塔Linux面板常用命令整理汇总
宝塔常用命令
喜欢建站的朋友,相信大家都对宝塔面板不陌生,它对小白还是老鸟好像都挺友好的。 不管是里面的插件还是方便性,都受大家伙的喜爱,于是就有了这一次关于宝塔常用命令的文章,希望大家喜欢。
是小北a
2022/03/28
6080
宝塔面板7.9.0强制登录绑定 Linux/Windows
使用面板的初衷是省时间,不需要绑定时强制登录绑定就是浪费时间,用户体验负分,后续考虑使用替代品aapanel
无刺鱼
2022/04/15
1.9K0
相关推荐
宝塔面板7.9.0已发布(Linux)-星泽V社
更多 >
LV.0
这个人很懒,什么都没有留下~
目录
  • 一,类的基本认识
    • (一)类的定义
    • (二)访问限定符
    • (三)类域
    • (四)实例化
      • 4.1 实例化概念
      • 4.2 对象大小
    • (五)this指针
  • 二,类的成员函数
    • (一)类的默认成员函数
    • (二)构造函数
      • 2.1 初始化列表
    • (三)析构函数
    • (四)拷贝构造函数
      • 4.1 拷贝
      • 4.2 拷贝构造函数
    • (五)const成员函数
  • 三,运算符重载
    • 示例一
    • 示例二
    • 示例三
    • 6.1 赋值运算符重载
    • 6.2 取地址运算符重载
  • 四,类型转换
  • 五,static成员
  • 六,友元
  • 七,内部类
  • 八,匿名对象
  • 九,对象拷贝时的编译器优化
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档