【C++知识点总结全系列 (04)】:C++类的详细总结与分析

1、类的概述

(1)What(什么是类)

本质是一个数据结构 = 变量 + 函数

(2)Why(类的作用)

类是面向对象变成的核心概念,它提供了一种组织和管理代码的方式,便于实现对世界的抽象和建模

(3)类的初始化

类是通过构造函数形成对象进行初始化的

(4)类的组成

A.成员变量

类的静态成员变量

静态成员和类相关联,不和对象相关联

类的一般成员变量

一般成员属于对象,和对象相关联

类的可变成员变量

永远不可能是 const,即使它是 const 类对象的成员变量,因此一个 const 成员函数可以改变可变数据成员

B.成员函数

const成员函数

这种类型的成员函数不能改变类对象的数据,只能访问类对象

成员函数的重载

同一作用域内,定义了两个或以上函数名相同,但函数的参数列表不同的函数

多态与重载的区别

  • 定义:重载-多个相同的函数名,但参数列表和返回值类型存在着不同;多态-基类 对需要重写的函数进行 virtual 修饰,派生类重写 virtual 函数
  • 绑定时机:重载是一种静态绑定-根据传入的参数类型和数量在编译时进行抉择;多 态是一种动态绑定-根据对象的类型在运行时进行抉择

注意:在类内部定义的成员函数自动是inline模式,也可以显式地指定成员函数为inline

2、类成员的访问权限

  • public:公有成员在类内部、类的对象和类的派生类中都是可访问的
  • protected:保护成员在类内部和派生类中都是可访问的,但类的对象不可访问
  • private:私有成员只在类内部可访问,对于类的对象和派生类不可访问
权限关键字类内部能否访问派生类能否访问外部能否访问
public可以访问可以访问可以访问
protected可以访问可以访问不可以访问
private可以访问不可以访问不可以访问

3、友元

(1)What(什么是友元)

在C++中,被关键字friend修饰的类成员方法(或类对象),被称为友元函数(或友元类)

(2)Which(有哪些友元)

  • 友元函数
class A{
private:
	int iCount;
public:
	friend int getCount(const A &a);
}
int getCount(const A &a)
{
	return a.iCount; //友元函数可以直接访问对应类的私有成员(变量和方法)
}
  • 友元类
class BB; //仅作声明,避免AA类中在传参的时候报错
class AA {
private:
	int iCount;
public:
	AA(int iCount_) :iCount(iCount_) {}
	friend int getCount(const AA& a) {
		return a.iCount;
	}
	int getBBTimes(BB& BObj);
};
class BB {
private:
	int iTimes;
public:
	friend class AA;
	BB(int iTimes_) :iTimes(iTimes_) {}

};
int AA::getBBTimes(BB& BObj)
{
	return BObj.iTimes;
}
void main()
{
	AA aObj(100);
	int iVal = getCount(aObj);
	std::cout << iVal << std::endl;
	BB bObj(1);
	int iTimes = aObj.getBBTimes(bObj);
	std::cout << iTimes << std::endl;
}

(3)Why(友元的作用)

  • 访问私有成员:友元的主要作用是允许其他类或函数访问当前类的私有成员
  • 增强类的交互:友元关系可以增强不同类之间的交互能力
  • 降低代码依赖性提高封装性:通过将某个类或函数声明为友元,可以减少对外界暴露的接口

4、聚合类

(1)What(什么是聚合类)

本质是一个自定义类型的数据结构(结构体或类),但聚合类有以下特性:

  • 所有的成员都是public
  • 没有任何构造函数
  • 没有基类
  • 类内部没有初始值

(2)Why(聚合类的作用)

主要目的是提供一种简单且高效的方式来组织和管理数据。它们通常用于表示简单的 数据结构,如结构体或记录类型,而不涉及复杂的行为和操作

(3)How(如何定义和使用聚合类)

struct Point{
	double dx;
	double dy;
};
Point pt01 = {1.1, 2.2};
Point pt02{3.3, 4.4};

5、嵌套类

(1)What(什么是嵌套类)

定义或声明在类内部的类,它可以访问外部类的私有成员,并能在外部类作用域内使用

(2)Why (嵌套类的作用)

封装和隐藏:将嵌套类进行私有化,实现封装和隐藏
组织和结构化:将一个类嵌套到另一个类,可以清晰的表达两个类之间的关系
实现设计模式:如工厂模式、建造者模式等
命名空间嵌套:有助于避免命名空间冲突

(3)How(如何使用嵌套类)

class Student 
{
private:
	string name;
	int age;
...
}
class Grade
{
private:
	Student student; //Student在类Grade中声明,Student属于类Grade的嵌套类
...
}

6、局部类

(1)What(什么是局部类)

定义在函数内部或代码块中的类被称为局部类,它的作用域是该函数内部或代码块中

局部类具有以下特性:

  • 不允许存在静态的成员
  • 只允许访问外层作用域的静态变量、枚举和类型名
  • 可以访问全局变量(使用:: ):😃

(2)Why(局部类的作用)

  • 封装:隐藏细节
  • 避免命名冲突:由于只对作用域可见,有助于避免命名冲突,提供更好的隔离性
  • 增加代码的可维护性

7、union类

(1)What(什么是union类)

本质是一种类,这种类有以下特点:union类可以包含多个成员,但同一时刻只能使用其中一个成员,当其中一个成员被赋值时,其它成员就会变成未定义的状态

(2)Why(union类的作用)

节省内存:当需要存储多种内存数据,但是每次只使用其中一种时
灵活处理异构数据:提供了一种灵活的数据结构,在无需定义多个变量或复杂数据结构的情况下存储和操作不同类型的数据

(3)How(如何使用union类)

struct Rectangle{ float height; float width; }; 
struct Circle { float radius; };
// 定义 union 类 
union Shape {
	Rectangle rect; 
	Circle circle;
};
float calculateArea(const Shape &shape, string mode) 
{ 
	if (mode == "circle") 
		return 3.1415926 * shape.circle.radius * shape.circle.radius; 
	if (mode == "rect")  
		return shape.rect.width * shape.rect.height;
	return 0;
}
// 创建 union 对象 Shape shape;
string mode = "circle"; 
shape.circle.radius = 5;
cout << shape.rect.width << "," << shape.rect.height << endl; //此处会报错
std::cout << "圆的面积:" << calculateArea(shape, mode) << std::endl; // 打印:78.5398 
mode = "rect";
shape.rect.height = 4; 
shape.rect.width = 6;
std::cout << "矩形的面积:" << calculateArea(shape, mode) << std::endl; // 打印:24

(4)匿名的union类

A.What(什么是匿名的union类)

本质:一个union类,它具有以下特性

  • 一旦定义了匿名的union类,编译器就会为它创建一个未命名的对象
  • 作用域内能够访问大匿名union类的所有成员
  • 匿名的union类中的成员必须是public的
  • 匿名的union类中不能定义成员函数,只能作纯数据的结构体使用

B.How(如何使用union类)

class UnionBaseType
{
private: 
	enum {INT, DOUBLE, CHAR, STRING } tp; // 通过匿名枚举类型定义一个变量 tp 
	union {
		int ival; 
		double dval; 
		string sval; 
	}; // 外层作用域可直接访问匿名 union 中的成员 
public:
	// 默认构造函数 UnionBaseType() : tp(INT), ival(0) {} // 拷贝构造函数
	UnionBaseType(const UnionBaseType &obj)
	{
		tp = obj.tp; copyUnion(obj);
	}
	~UnionBaseType()
	{
		if (tp == STRING) sval.~string(); 
	}
	void copyUnion(const UnionBaseType &obj)
	{
		switch (obj.tp){
			case UnionBaseType::INT: ival = obj.ival; break; 
			case UnionBaseType::DOUBLE: dval = obj.dval; break; 
			case UnionBaseType::STRING: new (&sval) string(obj.sval); break;// 实现对对象的拷贝 
			default: break;
	} 

	// 对 int 成员进行赋值 
	UnionBaseType &operator=(int ival_) 
	{
		if (tp == STRING) sval.~string(); 
		tp = UnionBaseType::INT; 
		ival = ival_; 
		return *this;
	}
	// 对 double 成员进行赋值 
	UnionBaseType &operator=(double dval_) 
	{
		if (tp == STRING) sval.~string();
		tp = UnionBaseType::DOUBLE; 
		dval = dval_; 
		return *this;
	}
	// 对 string 成员进行赋值 
	UnionBaseType &operator=(const string &sval_) {
		if (tp == STRING) sval = sval_;
		else 
			new (&sval) string(sval_); 
		tp = UnionBaseType::STRING; 
		return *this;
	}
	// 展示信息 
	void show() const{
		if (tp == INT) cout << ival << endl; 
		if (tp == DOUBLE) cout << dval << endl; 
		if (tp == CHAR) cout << cval << endl; 
		if (tp == STRING) cout << sval << endl;
	}
}

8、类成员指针

(1)What(什么是类成员指针)

本质:一个指针,该指针指向类的非静态成员,包含类成员数据指针类成员函数指针

(2)Why(类成员指针的作用)

  • 类成员数据指针:用于访问和修改类的数据成员值
  • 类成员函数指针:调用类的成员函数

(3)How (如何使用类成员指针)

//类成员数据指针访问和修改类
auto Student::*p_name = &Student::strStuName;
Student zs(1001, "张三", 23};
std::cout<<zs.*p_name<<std::endl; //打印:张三
zs.*p_name = "zhangsan";
std::cout<<zs.*p_name<<std::endl; //打印:zhangsan(strStuName必须是public才能被修改)

//类成员函数指针,用于调用类的成员函数
void (Student::*pf_showInfo) ()  = &Student::showInfo;
zs.*pf_showInfo();

9、类的转换

(1)隐式转换

将一个类对象的类型转为另一种类类型。如果一个类定义了转换构造函数或者转换运算符,那么编译器可以自动执行隐式转换

class B
{
public:
	double dVal;

public:
	B(double dVal_) : dVal(dVal_) {}
	B(int iVal_) :dVal(iVal_) {}

};
class A
{
public:
	int iVal;

public:
	A() {};
	A(int iVal_) : iVal(iVal_) {}
	A(B bObj)
	{
		iVal = (int)bObj.dVal;
	}

};
void main()
{
	B bObj(3.14);
	A aObj=bObj; //隐式地调用A的转换构造函数,将B转换成A
	std::cout << aObj.iVal << std::endl; //打印:3
}

(2)禁止隐式转换

explicit A(B bObj){...}

将A的转换构造函数使用关键字explicit进行修饰之后,A aObj = bObj;将会报错

(3)显式转换

使用显式转换操作符 static_cast、dynamic_cast、reinterpret_cast 和 const_cast 来实现不 同类型之间的转换

A.static_cast

  • 用于基本的类型转换、类层次结构间的上行转换(派生类指针向基类指针的转换)
    和下行转换(基类指针向派生类指针的转换)
  • 在编译时进行类型检查,不提供运行时类型检查:
  • 编译时类型检查:编译时编译器会检查代码中的类型错误和不一致之处,并给出 相应警告或提示。这种类型检查主要发生在编译阶段,在生成可执行文件之前
  • 运行时类型检查:程序运行时根据对象的实际类型检查和处理类型相关的问题
  • 在执行向上转型和向下转型时不提供动态检查,因此不安全
// static_cast 向下转换 01:指向派生类对象
Base *base_ptr = new Drived(0, 01);
base_ptr->showInfo(); // (静态绑定)打印:BaseClass! 0 
base_ptr->virtual_showInfo(); // (动态绑定)打印:virtual method in DrivedClass! 1 
Drived *drived_ptr = static_cast<Drived *>(base_ptr);
drived_ptr->showInfo(); // 打印:DrivedClass! 1 
drived_ptr->virtual_showInfo(); // 打印:virtual method in DrivedClass! 1 
cout << "*****************************" << endl;

// static_cast 向下转换 02:不指向派生类对象,这样转没有意义,属于非法转换 
Base *base_ptr01 = new Base(1);
Drived *drive_ptr01 = static_cast<Drived *>(base_ptr01); 
drive_ptr01->showInfo(); // 打印:DrivedClass! -1163005939 
drive_ptr01->virtual_showInfo(); // 打印:virsual method in BaseClass! 1 
cout << "*****************************" << endl;

// static_cast 向上转换 
Drived *drive_ptr02 = new Drived(2, 22); 
drive_ptr02->showInfo(); // 打印:DrivedClass! 22 
drive_ptr02->virtual_showInfo(); // 打印:virtual method in DrivedClass! 22 
Base *base_ptr02 = static_cast<Base *>(drive_ptr02);
base_ptr02->showInfo(); // 打印:BaseClass! 2 
base_ptr02->virtual_showInfo(); // (动态绑定)打印:virtual method in DrivedClass! 22 
cout << "*****************************" << endl;

B.dynamic_cast

  • 用于类层次结构间的安全向下转换:基类强转为派生类(父类强转为子类)
  • 运行时类型检查,可以检查是否进行有效的类型转换
  • 要确保基类指针或引用实际上指向派生类对象,如果转换不可行,则返回空指针(对指针进行转换)或抛出 std::bad_cast 异常(对引用进行转换)
  • 只能用于含有虚函数的类
// dynami_cast 向下转换 01:不指向派生类 
Base *base_ptr03 = new Base(3);
// 要求基类指针指向的实际对象是派生类的对象。否则,转换将失败并返回空指针 
Drived *drive_ptr03 = dynamic_cast<Drived *>(base_ptr03);
if (drive_ptr03){
	drive_ptr03->showInfo(); 
	drive_ptr03->virtual_showInfo(); 
}
else 
	cout << "转换失败!" << endl; // 打印:转换失败! 
cout << "*****************************" << endl; 
// dynami_cast 向下转换 02:指向派生类
Base *base_ptr04 = new Drived(4, 44);
Drived *drive_ptr04 = dynamic_cast<Drived *>(base_ptr04); 
drive_ptr04->showInfo(); // 打印:DrivedClass! 44
drive_ptr04->virtual_showInfo(); // 打印:virtual method in DrivedClass! 44

C.reinterpret_cast

  • 不进行类型检查或安全性检查,完全依赖于程序员自己来确保类型转换的正确性
  • 用于指针或引用之间的类型转换,也可用于不同类型之间的转换,如将整型转换为指针类型。
  • 执行非常低级的转换,可能会导致未定义行为。

D.const_cast

  • 添加 const 属性:给常规变量、指针或引用添加 const 属性
  • 删除 const 属性:去除常规常量、顶层指针和引用的 const 属性
  • 不要修改去除了 const 的 nonConstValue 变量,因为其行为是未定义的
  • const_cast 去除 const 属性主要作用是赋值和用作函数参数传递,避免重载带有 const参数的函数

================================================================================

C++知识点总结全系列文章索引:
【C++知识点总结全系列 (01)】:数据类型、数据类型转换和变量
【C++知识点总结全系列 (02)】:C++中的语句、运算符和表达式详细总结
【C++知识点总结全系列 (03)】:函数(函数参数传递、可变参数、函数返回值等详细介绍)
【C++知识点总结全系列 (04)】:C++类的详细总结与分析 ========》 当前位置
【C++知识点总结全系列 (05)】:IO 类的详细总结和分析
【C++知识点总结全系列 (06)】:STL六大组件详细总结与分析- 配置器、容器、迭代器、适配器、算法和仿函数

【C++知识点总结全系列 (07)】:模板与泛型编程详细总结与分析
【C++知识点总结全系列 (08)】:面向对象编程OOP

  • 22
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值