C++ | 面向对象 | 类

news/2025/2/26 18:39:35

👻

👾语法格式

class className{
	Access specifiers:				// 访问权限
     	DataType variable;			// 变量
     	returnType functions() { }	// 方法
};

👾访问权限

class className {
	public:
		// 公有成员
	protected:
		// 受保护成员
	private:
		// 私有成员
};

👽公有成员 public

公有成员在外部可访问,可不使用任何成员函数来设置和获取公有变量的值

class Line {
	public:
		double length;
		void setLength(double len);
		double getLength(void);
};
double Line::getLength(void) { return length ; }
void Line::setLength(double len) { length = len; }

...
Line line;
// 使用成员变量(正确,因为 length 公有) 
line.length = 10.0;
cout << line.length <<endl;
// 使用成员函数
line.setLength(6.0);
cout << line.getLength() <<endl;

👽私有成员 private

私有成员在外部不可访问,不可查看,只有友元函数可以访问私有成员。没有使用任何访问修饰符默认私有。

class Box {
	public:
		double length;
		void setWidth(double wid);
		double getWidth(void);
	private:
		double width;
};
double Box::getWidth(void) { return width; }
void Box::setWidth( double wid ) { width = wid; }

...
Box box;
// box.width = 10.0; 	// [Error] 'double Box::width' is private
box.setWidth(10.0);  	// 使用成员函数设置宽度
cout << box.getWidth() <<endl;

👽受保护成员 protected

受保护成员与私有成员相似,但有一点不同,受保护成员在**派生(即子)**中是可访问的。

/* 基 */ 
class Box {
	protected:
		double width;
};

/* 派生 */
class SmallBox: Box {  
	public:
		void setSmallWidth(double wid);
		double getSmallWidth();
};
double SmallBox::getSmallWidth() { return width; }
void SmallBox::setSmallWidth(double wid) { width = wid; }

...
SmallBox box;
box.setSmallWidth(5.0);
cout << box.getSmallWidth() << endl;

👻成员函数

👾语法格式

  • 定义在定义内部

    class className{
         returnType functions(parameter list) { ... }
    };
    
  • 单独使用范围解析运算符 :: 定义

    class className{
         returnType functions(parameter list);
    };
    returnType class_name::functions(parameter list){ ... }
    

👾示例代码

  • 定义在定义内部

    class Box {
    	public:
    		double length;  // 长度
    		double breadth; // 宽度
    		double height;  // 高度
    		double getVolume() { return length * breadth * height; }
    };
    
  • 单独使用范围解析运算符 :: 定义

    class Box {
    	public:
    		double length;  // 长度
    		double breadth; // 宽度
    		double height;  // 高度
    		double getVolume();
    };
    
    double Box::getVolume() { return length * breadth * height; }
    

👻构造函数

  • 构造函数在每次创建的新对象时执行。
  • 构造函数的名称与的名称是完全相同的,不返回任何型。
  • 构造函数可用于为某些成员变量设置初始值

👾无参构造函数

👽语法格式

class className{
     className() { ... }
};

👽示例代码

class Line {
	public:
		Line() { cout << "Object is being created" << endl; }
	private:
		double length;
};

...
Line line;	// 输出:Object is being created

👾带参构造函数

👽语法格式

class className{
     className(parameter list) { ... }
};

👽示例代码

class Line {
	public:
		Line(double len) {
			length = len;
			cout << "Object is being created" << endl;
		}
	private:
		double length;
};

...
Line line;	// 输出:Object is being created

👾初始化列表构造函数

👽语法格式

有多个字段 A、B、C 等需要进行初始化:

class className{
     className(int a,int b,int c): A(a), B(b), C(c) { ... }
};

👽示例代码

class Line {
	public:
		Line(double len): length(len) { cout << "Object is being created" << endl; }
	private:
		double length;
};

...
Line line;	// 输出:Object is being created

等同语法:

Line(double len) {
	length = len;
	cout << "Object is being created" << endl;
}

注意:初始化成员时,是按声明的顺序初始化,而不是按初始化列表的顺序初始化,即:

class Base {
	public:
		// 按照 A -> B -> C 的顺序初始化
		int A;
		int B;
		int C;

		// 而不是按照 C -> B -> A 的顺序初始化
		Base(int a, int b, int c): C(c), B(b), A(a) {}
};

👻析构函数

  • 析构函数在每次删除所创建的对象时执行。
  • 析构函数的名称与的名称是完全相同的,在前面加 波浪号(~) 作为前缀,不返回任何值,不能带有任何参数。
  • 析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源

👾语法格式

有多个字段 A、B、C 等需要进行初始化:

class className{
     ~className() { ... }
};

👾示例代码

class Line {
	public:
		Line() { cout << "Object created" << endl; }  // 构造函数声明
		~Line() { cout << "Object deleted" << endl; } // 析构函数声明
};

...
Line line;

一个内可以有多个构造函数(相当于重载构造函数),只有一个析构函数

class Matrix {
	public:
		Matrix(int row, int col);       //普通构造函数
		Matrix(const Matrix& matrix);   //拷贝构造函数
		Matrix();                       //构造空矩阵的构造函数
		~Matrix();
};

👻拷贝构造函数

👾语法结构

class className{
	// obj是对象引用,用于初始化另一个对象
     className (const className &obj) { }
};
  • 参数 obj —— 本的引用

    • const 型引用 —— 既能以常量对象(初始化后值不改变的对象)作为参数,也能以非常量对象作为参数初始化其他对象

    • const 型引用 —— 一般不使用

👾被调用情况

  • 情况1:用一个对象初始化同的另一个对象时,调用被初始化的对象的拷贝构造函数

    Base a2(a1);	
    Base a2 = a1;
    
    class Base {
    	public:
    		int num;
    		// 一般构造函数
    		Base(int n) {
    			num = n;
    			cout << "General Constructor called" << endl;
    		}
    		// 拷贝构造函数
    		Base(const Base& a) {
    			num = a.num;
    			cout << "Copy Constructor called" << endl;
    		}
    };
    
    ...
         
    // 只调用a1的一般构造函数,输出 General Constructor called
    Base a1(1);	
    // 只调用a2的拷贝构造函数,输出 Copy Constructor called
    Base a2(a1);	
    Base a2 = a1;
    // 不调用
    a2 = a1;		
    

    注意,Base a2 = a1; 是初始化语句,不是赋值语句。

    赋值语句的等号左边是一个早已有定义的变量,不会引发复制构造函数的调用,

    Base a1(1);	// 调用a1的一般构造函数
    Base a2(a1);	// 调用a2的拷贝构造函数
    a2 = a1;		// 不调用,因为 a2 早已生成且初始化,不会引发拷贝构造函数
    

    只会调用被初始化的对象的拷贝构造函数,不会调用其一般构造函数

  • 情况2:函数参数是的对象,当函数调用时,调用对象的拷贝构造函数

    class Base {
    	public:
    		// 一般构造函数
    		Base() { cout << "General Constructor called" << endl; };
    		// 拷贝构造函数
    		Base(Base& a) { cout << "Copy constructor called" << endl; }
    };
    void Function(Base a) {}
    
    ...
    
    Base a;		// 调用a的一般构造函数,输出 General Constructor called
    Function(a);	// 调用a的拷贝构造函数,输出 Copy constructor called
    
    • 对象作为形参,函数被调用时,生成形参要用拷贝构造函数,会带来时间开销
    • 对象的引用作为形参,无时间开销,但有一定的风险,因为如果形参的值发生改变,实参值也会改变

    如果要确保实参值不改变,又希望避免拷贝构造函数的开销,解决办法是将形参声明为对象的 const 引用:

    void Function(const Base &a){
        ...
    }
    
  • 情况3:函数返回值是的对象,当函数返回时,调用对象的拷贝构造函数

    class Base {
    	public:
    		int num;
    		// 一般构造函数
    		Base(int n) {
    			num = n;
    			cout << "General Constructor called" << endl;
    		}
    		// 拷贝构造函数
    		Base(const Base& a) {
    			num = a.num;
    			cout << "Copy constructor called" << endl;
    		}
    };
    
    Base Func() {
    	Base a(4);	// 调用一般构造函数,输出 General Constructor called
    	return a;
    }
    
    ...
    
    int a = Func().num;	// 调用拷贝构造函数,输出 Copy constructor called
    

    有些编译器出于程序执行效率的考虑,编译时进行优化,函数返回值对象不用拷贝构造函数,不符合C++标准

👾深拷贝和浅拷贝

👽浅拷贝

  • 使用情况:基本型数据、简单对象
  • 原理:按位复制内存
// 基本型数据
int a = 10;
int b = a;
// 简单对象
class Base {
	public:
		Base(): m_a(0), m_b(0) { }
		Base(int a, int b): m_a(a), m_b(b) { }
	private:
		int m_a;
		int m_b;
};

Base obj1(10, 20);
Base obj2 = obj1;

👽深拷贝

  • 使用情况
    • 持有其它资源的(如动态分配的内存、指向其他数据的指针)
    • 标准模板库(STL)中 string、vector、stack 等
    • 创建对象时进行一些预处理工作,比如统计创建过的对象的数目、记录对象创建的时间等
  • 原理:显式定义拷贝构造函数
  • 过程
    • 会将原有对象的所有成员变量拷贝给新对象
    • 会为新对象再分配一块内存,并将原有对象所持有的内存也拷贝过来
  • 结果:原有对象和新对象所持有的动态内存相互独立,更改一个对象的数据不影响另一个对象
// 持有其它资源的(如动态分配的内存、指向其他数据的指针)
#include <cstdlib>
#include <iostream>
using namespace std;

class Array {
	public:
		Array(const int len) {
			m_len = len;
			m_p = (int*)calloc(m_len, sizeof(int));
		}

		Array(const Array& arr) {
			this->m_len = arr.m_len;
			this->m_p = (int*)calloc(this->m_len, sizeof(int));
			memcpy(this->m_p, arr.m_p, m_len * sizeof(int));
		}

		~Array() { free(m_p); }

		int operator[](int i) const { return m_p[i]; }
		int& operator[](int i) { return m_p[i]; }
		int length() const { return m_len; }

	private:
		int m_len;
		int* m_p;
};

void printArray(const Array& arr) {
	int len = arr.length();
	for(int i = 0; i < len; i++) { 
          if(i == len - 1) { cout << arr[i] << endl; } 
          else { cout << arr[i] << ", "; } 
     }
}

...

int main() {
	Array arr1(10);
	for(int i = 0; i < 10; i++)
		arr1[i] = i;

	Array arr2 = arr1;
	arr2[5] = 100;
	arr2[3] = 29;

	printArray(arr1); // 输出:0, 1, 2, 3, 4, 5, 6, 7, 8, 9
	printArray(arr2); // 输出:0, 1, 2, 29, 4, 100, 6, 7, 8, 9
}

// 创建对象时进行一些预处理工作
#include <iostream>
#include <ctime>
#include <windows.h>
using namespace std;

class Base {
	public:
		Base(int a = 0, int b = 0) {
			m_a = a;
			m_b = b;
			m_count++;
			m_time = time(nullptr);
		}

		Base(const Base& obj) {
			this->m_a = obj.m_a;
			this->m_b = obj.m_b;
			this->m_count++;
			this->m_time = time(nullptr);
		}

		static int getCount() { return m_count; }
		time_t getTime() const { return m_time; }

	private:
		int m_a;
		int m_b;
		time_t m_time;      //对象创建时间
		static int m_count; //创建过的对象的数目
};

int Base::m_count = 0;

...

int main() {
	Base obj1(10, 20);
	cout << "obj1: count = " << obj1.getCount() << ", time = " << obj1.getTime() << endl;
     // 输出:obj1: count = 1, time = 1740055359

	Sleep(3000);

	Base obj2 = obj1;
	cout << "obj2: count = " << obj2.getCount() << ", time = " << obj2.getTime() << endl;
     // 输出:obj2: count = 2, time = 1740055362

	return 0;
}

👻静态成员

👾静态成员变量

👽声明

使用 关键字 static成员声明为静态

class Box {
	public:
		static int objectCount; // 声明静态变量
		Box() { objectCount++; } // 每次创建对象时调用构造函数,静态变量值加 1
};

无论创建多少个的对象,静态成员只有一个副本,在的所有对象中是共享的。

👽定义

外部通过 范围解析运算符 :: 定义静态变量

class Box {
	public:
		static int objectCount; // 声明静态变量
		Box() { objectCount++; } 
};
// 仅定义却不初始化 静态成员
int Box::objectCount;

无论是否初始化,必须定义,否则报错:

(.rdata$.refptr._ZN3Box11objectCountE[.refptr._ZN3Box11objectCountE]+0x0): undefined reference to `Box::objectCount'

👽初始化

// 定义+初始化 静态成员
int Box::objectCount = 0;

若不初始化,则在创建第一个对象时所有静态数据初始化为零

👽访问

  • 使用 范围解析运算符 :: 访问静态变量
Box box1();    // 声明 box1
Box box2();    // 声明 box2
cout << "创建对象总数: " << Box::objectCount << endl;

👾静态成员函数

👽声明定义

  • 使用 关键字 static成员声明为静态
class Box {
	public:
		static int objectCount; // 声明静态变量
		Box() { objectCount++; }
		static int getCount() { // 声明定义静态函数
			return objectCount;
		}
};

  1. 普通成员函数有 this 指针,可以访问中的任意成员
  2. 静态成员函数没有 this 指针,只能访问静态成员变量其他静态成员函数外部的其他函数

👽使用

  • 使用 范围解析运算符 :: 访问静态变量
cout << "创建对象总数: " << Box::getCount() << endl; // 对象不存在时 也能被调用
Box box1();    // 声明 box1
Box box2();    // 声明 box2
cout << "创建对象总数: " << Box::getCount() << endl;

静态成员函数即使 对象不存在 也能被调用


👻友元

👾友元函数

👽声明

使用 关键字 friend 把函数声明为友元函数

class Box {
		double width;
	public:
		Box(double w): width(w) {}
		friend void printWidth(Box box);  // 在函数前使用关键字 friend
};

👽定义

class Box {
		double width;
	public:
		Box(double w): width(w) {}
		friend void printWidth(Box box);
};

/* printWidth() 不是任何的成员函数,但因为它是 Box 的友元,可直接访问该的任何成员 */
void printWidth(Box box) { cout << "Width of box : " << box.width <<endl;}

👽使用

Box box;
box.setWidth(10.0);
printWidth(box); 	// 使用友元函数访问Box中的私有成员

👾友元

👽声明定义

class Box {
	public:
		Box(double w): width(w) {}
		friend class BigBox;
	private:
		double width;
};

/* BigBox是Box的友元,它可以直接访问Box的任何成员 */
class BigBox {
	public :
		void printWidth(Box& box) { cout << "Width of box : " << box.width << endl; }
};

👽使用

Box box(10.0);
BigBox big;
big.printWidth(box); // 使用友元中的方法访问Box中的私有成员

👻 this 指针

  • this 指针是一个特殊的指针,它指向当前对象的实例,每一个对象都能通过 this 指针来访问自己的地址。
  • 当一个对象的成员函数被调用时,编译器会隐式地传递该对象的地址作为 this 指针。
  • 友元函数没有 this 指针,因为友元不是的成员
class MyClass {
	private:
		int value;
	public:
     	// 可以明确地告诉编译器想访问当前对象的成员变量,而不是函数参数或局部变量,避免命名冲突
		void setValue(int value) { this->value = value; } 
		void getValue() { return this->value; }
};

...
     
MyClass obj;
obj.setValue(42);
int value = obj.getValue();

http://www.niftyadmin.cn/n/5869069.html

相关文章

Node.js包管理工具选型指南

Node.js包管理工具选型指南 一、核心工具矩阵对比 1.1 主流工具特性对比&#xff08;2025版&#xff09; 工具名称最新版本安装速度磁盘效率安全特性适用场景推荐指数引用来源npm10.5.0⭐⭐⭐⭐基础校验小型项目/兼容性要求高⭐⭐⭐14Yarn4.1.0⭐⭐⭐⭐⭐⭐⭐完整性校验审计企…

2022年全国职业院校技能大赛网络系统管理赛项模块A:网络构建(样题6)-网络部分解析-附详细代码

目录 附录1:拓扑图 附录2:地址规划表 1.SW1 2.SW2 3.SW3 4.SW4 5.VSU 6.SW7 7.R1 8.R2 9.R3 10.AC1 11.AC2 12.EG1 13.EG2 附录1:拓扑图 附录2:地址规划表

【STL专题】优先级队列priority_queue的使用和模拟实现,巧妙利用仿函数解决优先级

欢迎来到 CILMY23的博客 &#x1f3c6;本篇主题为&#xff1a;优先级队列priority_queue的使用和模拟实现&#xff0c;巧妙利用仿函数解决优先级 &#x1f3c6;个人主页&#xff1a;CILMY23-CSDN博客 &#x1f3c6;系列专栏&#xff1a; C | C语言 | 数据结构与算法 | Linux…

解锁养生密码,拥抱健康生活

在快节奏的现代生活中&#xff0c;养生不再是一种选择&#xff0c;而是我们保持活力、提升生活质量的关键。它不是什么高深莫测的学问&#xff0c;而是一系列融入日常的简单习惯&#xff0c;每一个习惯都在为我们的健康加分。 早晨&#xff0c;当第一缕阳光洒进窗户&#xff0c…

python poetry添加某个git仓库的某个分支

命令行不太清楚怎么弄&#xff0c;但可以通过编辑pyproject.toml实现 实例&#xff1a; pypika-tortoise { git "https://github.com/henadzit/pypika-tortoise", branch "do-not-use-builder" } 参考&#xff1a;WIP Do not copy pypika query by h…

Chromedriver与Chrome版本映射表

‌Chromedriver与Chrome版本映射表‌如下&#xff1a; ‌Chrome 71-73版本对应Chromedriver 2.46‌‌Chrome 70-72版本对应Chromedriver 2.45‌‌Chrome 69-71版本对应Chromedriver 2.44‌‌Chrome 68-70版本对应Chromedriver 2.43‌‌Chrome 67-69版本对应Chromedriver 2.42‌…

深入理解 Kubernetes 命名空间:高效管理与隔离资源的关键

深入理解 Kubernetes 命名空间:高效管理与隔离资源的关键 目录 深入理解 Kubernetes 命名空间:高效管理与隔离资源的关键什么是命名空间?命名空间的作用1. 资源隔离2. 多租户支持3. 资源管理和策略控制4. 简化管理创建和使用命名空间命名空间与集群中的其他资源结语在 Kuber…

从哪里下载WinPrefetchView最安全?

WinPrefetchView 是一款用于读取和显示 Windows 系统中预读取文件&#xff08;Prefetch&#xff09;信息的工具&#xff0c;能够帮助用户了解系统启动时加载了哪些文件以及应用程序的运行情况。为了确保下载安全&#xff0c;建议从以下可信来源获取&#xff1a; 1. MajorGeeks…