c++(2)
C++ (2)
1. 类与对象
1.1 类与对象的概念
从c和c++的struct开始,c的struct结构体只能存在数据变量,而c++的struct体可以函数
1.1.1 类的封装
通过类可以封装对象的属性(特征,数据变量或引用)、行为(函数,类方法),可以通过访问权限(公开 public,私有的private,受保护protected)设置属性和行为的访问性。私有的或手保护的权限的(数据或方法)可以通过公开的方法(行为)进行操作。
类的封装主要的任务:
1. 把变量(属性)和函数(操作)合成一个整体,封装一个类中
2. 对变量和函数进行访问控制
public修饰的成员,在任何地方都可以访问。类的内部,所有的成员都可以访问,如果不使用子类的情况下,protected和private一样的。如果存在子类,则protected成员可以被访问。
定义类的语法:
class 类名{
[访问权限:]
//封装类成员的属性
//封装类成员的方法
};
【注意】类的定义是建议在全局区或头文件中
1.1.2 类的实例
类定义好之后,可以通过类,创建具体的对象,创建的类对象又称之为类的实例。
通过类创建的对象,对象即具有类的属性(数据)和方法
语法:
类名 对象名;
如:
#include <iostream>
#include <cstring>
using namespace std;
class Student
{
public:
int sid;
char name[32];
private:
//如果成员变量初始化值时,编译时指定标准为c++11
float score = 0;
public:
void showScore()
{
cout << "sid=" << sid << ",score=" <<score <<endl;
}
void addScore(float n);//只是声明
};
//实现类中声明的方法(函数)
void Student::addScore(float n)
{
score = n;
}
int main(int argc,char const *argv[])
{
Student s1;
s1.sid = 1001;
strcpy(s1.name,"disen");
//s1.score = 100;不能直接访问private成员
s1.addScore(100);//通过公开的方法修改private成员
s1.showScore();
return 0;
}
创建类对象时,在栈区分配内存空间,存放对象的属性(成员变量),当对象调用方法时,除了实参的数据之外,默认存在this指针,this指向调用的对象地址(内存空间),在方法中通过this指针可以操作对象自己的属性(成员变量)。
1.2 对象的构造与析构
1.2.1 构造函数与析构函数
对象的构造:创建类的对象时,默认调用类的某一个函数,进行内存空间的创建和初始化类的成员变量的值,这个函数称之为构造函数。
构造函数:无返回值(不能使用void),函数名同类名。默认的构造函数是无参函数。
对象的存储空间在栈区时,程序结束时,自动释放空间,在释放空间之前,会调用对象的析构函数,回收资源。
析构函数:无返回值,无参数,函数名同类名,但名称前加~
标识符。
【注意】构造函数可以存在多个(具有重载特性),析构函数只能有一个。构造函数和析构函数的访问权限尽量是public的。
1.2.2 构造函数的分类与调用
按参数类型:分为无参构造函数和有参构造函数
按类型分类:普通构造函数和拷贝构造函数(复制构造函数)
拷贝的构造函数:接收同类的其他对象,将其它对象中的数据复制到当前对象中。
【注意】如果没有显示地声明构造函数,则编译器自动添加一个无参的构造函数(隐式)。如果显示地声明构造函数,则编译器不会提供无参的构造函数。如果显示地声明构造函数,也应该提供一个无参的构造函数,可供创建类对象使用(类名 对象名
)。
构造函数的调用方式:
1)无参的调用
类名 对象名;
2)有参的调用
类名 对象名(参数列表);
3)匿名(无对象名) 调用
类名(参数列表) //创建了对象,但是没有名称
类名 对象名 = 类名(参数列表);
4)= 调用,针对单个参数的构造函数
类名 对象名 = 常量(字面量) //等价于 类名 对象名=类名(常量);
类名 对象名 = 同类的其他对象; //等价于 类名 对象名=类名(同类其它对象)
5)new方式
类名 *对象名 = new 类名(参数列表);
free(对象名)·
【注意事项】
1)创建的类的对象,如果是指针类型,则需要手动释放(free、调用析构函数、delete)
2)对于直接通过类创建的对象,不能手动释放,在对象的作用域之外自动释放(调用析构函数)
1.2.3 拷贝构造函数的调用时机
拷贝构造函数的调用时机:
1)类对象作为右值,赋值给定义类的对象时,会调用拷贝构造函数
2)函数的局部对象直接返回时,可能会调用构造函数
3)类对象作为函数的参数(以值的方式传递)时,可能会调用构造函数
当函数返回一个局部对象时,编译器做了优化,在函数外可以接收的(转化为引用),如果手动声明返回类型为对象的引用,反而会导致局部的对象在返回之后自动释放空间,使得外部使用时报段错误。
1.2.4 构造函数调用规则
默认情况下,c++编译器至少为我们写的类增加3个函数
1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝构造函数,对类中非静态成员属性简单值拷贝
如果用户定义拷贝构造函数,c++不会再提供任何默认构造函数,如果用户定义了普通构造(非拷贝),c++不再提供默认无参构造,但是会提供默认拷贝构造。
1.2.5 深拷贝与浅拷贝
浅拷贝:只复制一层内存空间
深拷贝:所有层次空间全部复制一份
1.2.6 多个对象构造和析构
1.2.6.1 初始化列表
构造函数:主要用于创建类的对象,在定义构造函数时,C++中提供了初始化列表的语法,用于初始化成员变量的值。
类名(参数列表):成员名(参数名),成员名2(参数名2),...{}
1.2.6.2 类对象作为成员
#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout << "A()" << endl;
}
A(int x)
{
cout << "A(int)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
};
class B
{
public:
B() // 构造函数用于初始化成员数据, 说明成员变量先创建
{
cout << "B()" << endl;
}
//B(int x):a(x) //指定a对象的构造函数进行数据初始化
B(int x)
{
cout << "B(int)" << endl;
}
~B() // A的对象是B的空间中,A是B空间释放之后就失效,也会释放空间
{
cout << "~B()" << endl;
}
private:
A a; // A类的对象作为B类的成员
};
int main(int argc, char const *argv[])
{
B b1;
// 输出什么?
return 0;
}
说明:
1)创建B空间时,成员变量a的内存空间会自动创建
2)当成员变量a的空间创建完成后,再调用构造函数,进行数据初始化
3)当有B空间释放时,成员变量a的作用域失效,则也会释放空间
1.2.7 explicit关键字
c++提供了关键字explicit,禁止通过构造函数进行的隐式转换(格式: 类 对象=值)。
声明为explicit的构造函数不能在隐式转换中使用。构造函数的参数只能存在一个或第一个没有默认值(其它参数都具有默认值)的情况
1.2.8 动态创建对象
动态创建对象方式:
1)malloc,realloc,calloc在堆中创建空间,使用完之后,需要手动释放free()
当内存空间创建完之后,进行数据的初始化或者调用自定义的初始化函数(默认不会调用构造函数,构造函数不能显示调用)
2)new关键字创建对象空间
使用new在堆中创建空间之后,自动调用对象的构造函数进行数据初始化
使用new创建的对象,可以通过delete关键字来调用对象的析构函数释放内存
1.2.9 扩展new和delete
用于数组的new和delete
创建数组: 堆空间中创建
数据类型 *指针变量名 = new 数据类型[个数];
数据类型 *指针变量名 = new 数据类型[个数]{元素,...};
删除new创建的数组:
delete[] 指针变量名;
动态创建类对象的数组时,如果没有指定类对象的创建方式时,必须提供一个无参的构造函数:
类名 *p = 类名[个数];
类名 *p = 类名[个数]{类名(参数列表),...};
2. 类的静态成员
类的成员声明时,前面(最左边)可以使用static关键字,将其声明为类的静态成员。
静态成员:静态成员变量和静态成员函数
【注意】类的静态成员属于类,不占类对象的空间,是所有类对象共享的。类的静态成员函数,只能访问静态成员变量。
静态成员的声明、初始化与访问
#include <iostream>
using namespace std;
class A
{
public:
static int x;
};
int A::x = 20;//类中声明类外定义
int main()
{
A a1,a2;
//访问类的静态成员:1)A::x,2)对象名.x
cout << "A::x=" << A::x <<endl;
A::x = 30;
cout << "a1.x=" << a1.x << endl;
cout << "a2.x=" << a2.x << endl;
return 0;
}
class A
{
public:
int m;
static int x;
static void add(int n)
{
// m = 10; // error, 静态成员函数不能访问非静态成员
x += n;
cout << "static x=" << x << endl;
}
};
类的单例设计模式
私有化构造函数和拷贝函数,提供一个public的静态函数返回类的指针对象,声明一个静态的类的指针对象
#include <iostream>
#include <cstdin>
using namespace std;
class A//单例的类,类的对象运行期间有且只有一个对象
{
private:
int x;
A(int x){this->x=x;}
A(const A &other)
{
x=other.x;
}
public:
static A *getInstance(int x=0)
{
if(instance == NULL)
{
instance = new A(x);
}
return instance;
}
void show()
{
cout << "x=" << x <<endl;
}
public:
static A *instance;
};
A *A::instance = NULL;//定义初始化,在静态全局区
int main()
{
A *a1 = A::getInstance();
a1->show();
A*a2 = A::getInstance();
a2->show();
cout << "a1 addr=" << a1 << endl;
cout << "a2 addr=" << a2 << endl;
delete A::instance;//释放单例对象的空间
return 0;
}
3. 对象的存储
3.1 类的成员变量与函数的存储
类中的非静态成员存储与类的对象存在一起的,类的成员函数存储在类对象(代码区)。
一个类的大小由类的非静态成员的决定的。
当前调用成员函数时,从类存储的位置(代码区)中调入的栈中,为函数的形参变量分配空间(局部变量),同时也会调用成员函数的对象封装成当前类的指针,这个指针即为this。
3.2 this指针
当某一个对象调用成员函数时,将对象转化为this指针,传入到成员函数中,以区分另一个对象调用的成员函数,方便操作自己对象的成员变量。当形参和成员变量同名时,也可用this指针来区分。
在类的非静态成员函数中返回对象本身,可使用return *this,可以通过这种实现对象的链式编程(a1.a().b().c()...😉,如cout属于链式编程风格(cout<<""<<""<<"").
3.3 const修饰成员函数与类对象
当const修饰成员函数时,函数内不能修改普通成员变量,但可以修改mutable修饰的成员变量。
当const修饰类对象时,只能读成员变量,不能修改成员变量的值,可以调用const修饰的成员函数,不能调用普通成员函数。
4. 友元
4.1 友元语法
使用friend关键字,可以修饰其他类、类成员函数,全局函数都可声明为友元
【注意】
友元函数不是类的成员,不带this指针
友元函数可访问对象任意成员属性,包括私有属性
4.2 全局友元函数
class B
{
//声明全局函数的友元
friend void show(B &b);//将全局的show()函数设置为当前类的友元函数
private:
int x;
public:
B(int x)
{
this->x=x;
}
void show()
{
cout <<x <<endl;
}
};
//全局的友元函数
void show(B &b)
{
//b的私有成员
cout << "b.x="<<b.x<<endl;
}
4.3 友元的类成员函数
类的某一个成员函数作为友元函数时,不能在类定义,只能声明
class B;
class C
{
public:
//在此函数内,要访问B类中所有成员,将此函数在B类中声明友元
//先声明,不能实现
void showB(B &b);
};
class B
{
//声明友元的成员函数
friend void C::showB(B &b);
private:
int x;
public:
B(int x)
{
this->x=x;
}
void show()
{
cout<<x<<endl;
}
};
//定义或实现友元的成员函数
void C::showB(B &b)
{
cout <<b.x<<endl;
}
4.4 友元类
class B;
class C
{
public:
void showB(B &b);
};
class B
{
friend class C;
private:
int x;
public:
B(int x)
{
this->x=x;
}
void show()
{
cout <<x <<endl;
}
};
//定义或实现友元的成员函数
void C::showB(B &b)
{
cout << "b.x="<<b.x<<endl;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)