3-8第一次订改 对部分细节完善 逻辑结构优化 错别字更改
3-12第二次复习
目录
二、深入理解访问限定符" :: "及其与" . "的对比关系
三、类的四大默认函数(构造函数、析构函数、拷贝构造函数、符号重载函数)
一、类的定义
依据关键字class与struct创建类 类的"}"后加";" 。
类中以类访问限定符:公共域(public)私有域(private/protected)划分:
其中class默认为私有域 struct默认为公有域 //其中"默认"指不加访问限定符情况下成员的属性。
class Data
{
//class定义类 若不加类访问限定符 默认私有域
/*int _length; //" _ "以区分类内属性值与外界引入值
int _width;
int _height;*/
public:
void Init(int length, int width, int height)
{
_length = length;
_width = width;
_height = height;
}
void Print()
{
std::cout << "长为:" << _length << std::endl \
<< "宽为:" << _width << std::endl \
<< "高为:" << _height << std::endl;
return;
}
private:
//class开头若不加类访问限定符 默认私有域
int _length; //" _ "以区分类内属性值与外界引入值
int _width;
int _height;
};
上述代码中为何不直接初始化_length、_width、_height ? 偏偏嵌套于Init中?
答:类内变量无论是public还是private都不能直接初始化 只能定义 若要初始化必须套在函数中。
类内方式定义声明:
前言:类的实例化——Date d1; //类的实例化创建了对象。
class Student {
public:
static int total; // 静态变量 成员声明
static const int MAX = 100; // 静态常量 可以类内初始化
};
int Student::total = 0; // 静态变量 成员必须在类外定义/初始化
类内常量的初始化与非全局函数的初始化:

若理解困难有链接如下进入 找到目录相应章节即可:
类与命名空间可以相互嵌套:
namespace Graphics
{
class point
{
//……
};
namespace Shapes //形状
{
//……
}
}
二、深入理解访问限定符" :: "及其与" . "的对比关系
在类内声明的非全局函数(不论静态还是非静态),在类外定义时都需要 :: 访问。
(博文末尾有思维导图总结类内什么可以初始化什么不可以)
关于类名Date和对象名d1的使用:
| 对比项 | Date:: | d1. |
|---|---|---|
| 正式名称 | 域作用限定符 | 成员访问运算符 |
| 左侧 | 类名 (如 Date) | 对象名 (如 d1) |
| 右侧 | 静态成员 | 非静态成员 |
| 本质 | 告诉编译器去类的静态区找 | 告诉编译器去对象的内存找 |
| 内存 | 不依赖具体对象,类只有一份 | 依赖具体对象,每个对象一份 |
| this指针 | 没有 this 指针 | 有 this 指针指向当前对象 |
| 能否访问非静态 | ❌ 不能 | ✅ 能 |
| 能否访问静态 | ✅ 能 | ✅ 也能(语法糖) |
代码举例:
class Date {
public:
// 静态成员(属于类)
static int count;
static void showCount() { cout << count << endl; }
// 非静态成员(属于对象)
int year;
void print() { cout << year << endl; }
};
int Date::count = 0; // 静态成员定义
int main() {
// 1. 通过类名访问静态成员 ::
Date::count = 100;
Date::showCount(); // 输出 100
// 2. 通过对象访问非静态成员 .
Date d1, d2;
d1.year = 2024;
d2.year = 2025;
d1.print(); // 输出 2024
d2.print(); // 输出 2025
return 0;
}
对象内部成员的存储:
类中一切成员变量都占用内存(无论公有/私有域) 而成员函数不会占用内存。
对象大小的计算规则(内存对齐规则):
1.第一个成员在结构体偏移量为0的地址处。
2.其余成员变量都要对齐到某个数字(对齐数)的整数倍位置。
3.每个变量都有对齐数。对齐数 == 编译器默认对齐数(如VS2022 :8) 与 成员变量的类型大小取较小值。 //为什么取对齐数? 因为机器读取数据是以某个整数来整数倍整数倍地读取(提高效率) //为什么取最小值因为较小值在相对增加存储密度的基础上保证读取次数少。
4.最后取大小时取所有类型变量对齐数最大的那个的整数倍数。
this指针
作用:链接类中相关属性值使得函数之间访问便利。
this指针隐藏于函数参中像:
// void Init(Date* const this, int year, int month, int day) ;
我们常写void Init(int year, int month, int day) ; 其实是this指针被隐藏的结果。
存在形式:隐藏于函数、变量名前 代码如下:
class Date
{
public:
// void Init(Date* const this, int year, int month, int day)
void Init(int year, int month, int day)
{
//我们写的是:
_year = year;
_month = month;
_day = day;
//实际上:
//this->_year = year;
//this->_month = month;
//this->_day = day;
}
void Print()
{
//cout << this->_year << this->_month << this->_day << endl;
//相应的函数参中也存在Date* const this 形参
cout << _year << _month << _day << endl;
}
三、类的四大默认函数(构造函数、析构函数、拷贝构造函数、符号重载函数)
默认函数:在创建类后即使不显示添加相关功能函数 编译器也会自动创建的类内函数。
它们拥有特殊的名称、构造方式。作用是为了对类类型对象的直接操作更加方便。
类默认成员函数的逻辑图:
(1).类的初始化和清理函数
功能: 由于在执行中常常有手动调用初始化函数如:void Init() 也会有调用销毁函数 void Destroy();
C++规定者认为过于麻烦而创建下面两类相关功能函数。
1/4.构造函数
功能:当我们创建一个新的对象时无需手动调用初始化函数来初始化各成员变量,编译器会自动调用初始化函数。
构造函数的定义要求:
1.函数名与类名保持一致,无返回值(连void 都没有)。
2.类型有 无参构造函数 全缺省构造函数 编译器自动生成的无参构造函数,构造函数可重载(见以下代码)。
3.编译器自动生成的构造函数是无参构造函数。
4.编译器自动生成的无参构造函数的初始化类型有 内置类型(栈内元素 动态申请) 自定义类型(像栈 struct)。此处的构造函数对内置类型的变量初始化(是浅拷贝(一个一个字节地拷贝实现)) 对自定义类型调用自定义类型的构造函数。
****深拷贝:指特殊类型的拷贝像动态申请的变量深拷贝就是再次申请分配空间***
//无参构造函数
Date::Date() //err 类外定义一定要类访问限定::
{
_year = 2026;
_month = 3;
_day = 4;
cout << "无参构造函数执行完成" << endl;
}
//无参构造函数的重载 //此第二个函数一定是在无参构造函数的定义之后出现
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
cout << "无参构造函数的重载执行完成" << endl;
}
/*
//全缺省构造函数
Date::Date(int year = 2007, int month = 1, int day = 28)
{
_year = year;
_month = month;
_day = day;
cout << "全缺省构造函数完成" << endl;
} */
一旦上两个函数存在此函数一定不能存在! 试分析原因:
答案:当打算使用第二个函数时编译器无法明确调用哪一个函数。
2/4.析构函数
功能:生命周期结束后自动调用析构函数 类似于 void Destrory() 将动态申请的 值得重置的值进行重置 (如:链表中的动态申请的int* a与int size ; int capacity 将a释放free并且将size与capacity 重置为0 )。
析构函数的定义要求:
1.函数名为在类名前加"~" 和构造函数一样无返回(连void也没有)。
2.析构函数 无参数 此类型函数有且只有一个。
***一个项目中如果没有动态申请的资源那么析构函数可以不写 仅使用编译器自动生成的析构函数就可以了***
#include <iostream>
using namespace std;
Date::~Date()
{
free(a);
a = nullptr;
size = 0;
capacity = 0;
}
析构的顺序:
C++规定: 先定义的对象 后析构。
(2).拷贝和运算符函数
※3/4.拷贝构造函数
功能:实现类类型的拷贝 (如:实现对象d1的数据(主要为属性值(private))拷贝到对象d2)。
理解:拷贝构造函数是构造函数的重载,实现的是对象的从无到有 。
函数定义:
1.拷贝构造函数是构造函数的重载 。
※2.参数:至少一个(若有额外的参数必须缺省) 自身类类型的引用 (const修饰)。
//拷贝构造函数
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
//调用方式
Date d1;
//1.
Date d2 = d1;
//2.
Date d2(d1);
参数传递的是引用若传递值会引发无穷递归 。原因如下:
传值传参需要调用拷贝函数而这个函数就是拷贝函数导致始终进行这个函数的调用。
4/4.符号重载函数
功能:通过符号重载函数解决类类对象间不能便捷地符号运算的问题。
运算符重载函数
基本规定:
1.不作成员函数时参数个数与相应运算符的运算对象数目一致(一元运算符一个参数二元运算符二个参数),作成员函数参数个数比正常数目少一个因为第一个运算对象默认传给隐式的this指针 这样在函数 内可以直接调用相应属性进行运算。
2.重载后优先级与结合性与原运算符保持一致。
3.规定:" .* :: sizeof ?: . "这5个运算符不能重载。 (其中".*"为指针访问运算符了解就行)。
4.运算符重载函数至少有一个类类型参数。
5.重载++ / -- 时为区分前置与后置在后置的情况下加以"int"(如:Date& operator++(Date& d,int);)。
6.重载<< / >> (此处指:流插入运算符 流提取运算符) 需要重载为全局函数(常用方法是构建友元函数 如例2:)。
例1.重载运算符函数 < 比较两日期的大小关系:
//***********************************
bool Date::operator < (const Date & d) const //第二个const 表示不修改成员变量(*this)
//***********************************
{
if (_year < d._year)
return true;
else if (_year > d._year)
return false;
else //year相等
{
if (_month < d._month)
return true;
else if (_month > d._month)
return false;
else //月相等
{
if (_day < d._day)
return true;
return false;
}
}
}
例2.重载<<与>>示例:
//.h
class Date
{
//友元函数的声明位于类内定义在类外
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
};
//.cpp
ostream& operator<<(ostream& out,const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
istream& operator>>(istream& in, Date& d)
{
cout << "请输入年、月、日:" << endl;
in >> d._year >> d._month >> d._day;
return in;
}
********************************************************
友元函数类内声明就相当于全局函数 全局函数不用加类作用限定符::
********************************************************
赋值运算符重载函数
基本规定:
1.赋值运算符必须重载为成员函数,赋值运算符的参数建议写成const当前类类型引用减少拷贝。
2.有返回值且建议写成类类型的引用 引用返回可以提高效率 返回的目的是为了支持连续赋值。
3.没有显示实现时编译器会自动生成一个默认赋值运算符重载,默认赋值运算符重载会对内置类型进行浅拷贝(一个一个字节的拷贝),对自定义类型会调用其赋值运算符重载函数。
//重载" = " 赋值运算符
Date& Date::operator=(const Date& d) //参数加入const表示不修改该参
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this; //返回是为多重赋值 返回引用是为提高效率
}
拷贝构造函数与赋值运算符重载(易错点!)
直接看代码判断:
//A.
Date d1;
Date d2;
d2 = d1;
//B.
Date d1;
Date d2 = d1;问:A和B谁是赋值重载?
答案:A
两种角度进行原因分析:
一.
拷贝构造函数的调用执行构造的功能 B中代码解释为 先构造d2(初始化)再拷贝。
二.
B项:
d2还在创建没有构造完成
用已存在的d1初始化d2
调用的是拷贝构造函数Date d2(d1);
7983

被折叠的 条评论
为什么被折叠?



