现在人们打棋牌麻将谁不想赢?手机微乐麻将必赢神器但是手机棋牌麻将是这么好赢的吗?在手机上打棋牌麻将想赢,不仅需要运气,也需要技巧。掌握的棋牌麻将技巧就...
2025-09-09 0
各位C++战士们,欢迎来到面向对象的“觉醒时刻”!
1.1、面向过程和面向对象的初步认识
编程思想的核心差异,从 C 语言和 C++ 的对比中可见一斑:
面向对象编程(OOP)的核心思想是 “万物皆为对象”,将现实世界的事物抽象为程序中的对象,每个对象包含两部分:
面向对象的三大特性是其强大的核心:
简言之,面向对象通过 “对象” 作为基本单元,实现数据和行为的有机结合,让程序结构更清晰、易维护、可扩展。
C 语言的结构体只能定义变量,而 C++ 对结构体进行了扩展:结构体中不仅可以定义变量,还可以定义函数。例如,用 C++ 实现栈:
typedef int DataType;struct Stack { // 成员变量(属性) int top; int capacity; DataType* array; // 成员函数(行为) void Init() { top = 0; array = (DataType*)malloc(sizeof(DataType) * 4); if (array == nullptr) { perror("malloc failed"); return; } capacity = 4; } void Push(DataType x) { if (top == capacity) { // 扩容 DataType* tmp = (DataType*)realloc(array, sizeof(DataType) * (capacity * 2)); if (tmp == nullptr) { perror("realloc failed"); return; } array = tmp; capacity *= 2; } array[top++] = x; } DataType Top() { if (top == 0) { cout << "stack is empty" << endl; return -1; } return array[top - 1]; }};int main() { Stack ss; // C++中可省略struct关键字 ss.Init(); // 直接通过对象调用成员函数,无需传参 ss.Push(1); ss.Push(2); cout << ss.Top() << endl; // 输出2 return 0;}
上述代码中,Stack结构体同时包含了变量(top、capacity等)和函数(Init、Push等),且调用函数时无需像 C 语言那样传递结构体指针 —— 这是因为函数内部可以直接访问结构体的成员变量。
不过,C++ 中更推荐用class关键字定义类(而非struct),两者功能类似,但存在访问权限的默认差异(后续会讲)。
专注Linux、Unix、C/C++后端开发、面试题等技术知识讲解
类的基本语法如下:
class ClassName { // 类体:成员变量(属性)和成员函数(方法)}; // 注意末尾的分号
1.声明和定义全部放在类体中
成员函数在类内定义时,编译器可能将其视为内联函数(inline),适合简单函数。
class TestClass {public: void Print() { // 类内定义,可能被视为内联 cout << _a << " " << _b << endl; }private: int _a; // 成员变量命名建议:加下划线区分 int _b;};
2.声明放在.h文件,定义放在.cpp文件
类声明和定义分离,适合复杂函数,避免重复编译。类外定义成员函数时,需用类名::指定作用域。
// test.h(声明)class TestClass {public: void Print(); // 声明private: int _a; int _b;};// test.cpp(定义)#include "test.h"void TestClass::Print() { // 类外定义,需加TestClass:: cout << _a << " " << _b << endl;}
class Person {public: void SetName(string name) { _name = name; // _name是成员变量,name是参数,清晰区分 }private: string _name; // 成员变量加下划线};
C++ 通过访问限定符控制成员的访问权限,实现 “封装” 的核心功能。常用的访问限定符有三种:
访问限定符的作用域:
从限定符出现的位置开始,到下一个限定符出现为止;若没有下一个限定符,则到类体结束(})为止。
class 与 struct 的区别:
示例:
class A { int _a; // 默认private,类外不可访问public: int _b; // public,类外可访问private: int _c; // private,类外不可访问};struct B { int _x; // 默认public,类外可访问private: int _y; // private,类外不可访问};int main() { A a; a._b = 10; // 正确(public) // a._a = 20; // 错误(private,类外不可访问) B b; b._x = 30; // 正确(public) // b._y = 40; // 错误(private,类外不可访问) return 0;}
封装的本质:将数据(成员变量)和操作数据的方法(成员函数)有机结合,隐藏内部实现细节,仅通过公开接口与外部交互。
例如,手机的封装:用户无需知道内部芯片、电路的工作原理,只需通过屏幕、按键(接口)操作即可。
C++ 中,封装通过访问限定符实现:
类定义了一个独立的作用域(类域),所有成员都属于这个作用域。在类外定义成员时,需用::(作用域操作符)指明所属类域。
示例:
class Person {public: void PrintInfo(); // 声明:属于Person类域private: char _name[20]; int _age;};// 类外定义:需用Person::指定作用域void Person::PrintInfo() { cout << _name << " " << _age << endl; // 可直接访问类内成员}
类的实例化:用类类型创建对象的过程。
示例:
class Person {public: char _name[20]; int _age;};int main() { Person p1; // 实例化对象p1 Person p2; // 实例化对象p2 p1._age = 18; // 对象p1的年龄 p2._age = 20; // 对象p2的年龄(不同对象的成员变量独立) return 0;}
上述代码中,Person类本身不占内存,p1和p2是实例化的对象,各自占用内存存储_name和_age。
类的空间大小仅取决于成员变量,成员函数存储在公共代码区(所有对象共享同一份函数代码)。
#include <iostream>using namespace std;class A {}; // 空类class B {private: int _a; // 成员变量};class C {public: void Print() { cout << _a << endl; } // 成员函数(不占对象空间)private: int _a; // 成员变量};int main() { cout << sizeof(A) << endl; // 输出1 cout << sizeof(B) << endl; // 输出4 cout << sizeof(C) << endl; // 输出4 return 0;}
类的成员变量存储遵循结构体对齐规则(因为类的底层存储可视为 “带函数的结构体”):
示例:
class D {private: char _c; // 对齐数1(自身大小) int _i; // 对齐数4(min(4, 8)) double _d; // 对齐数8(min(8, 8))};// 内存布局:// _c:偏移0(1字节)// 填充3字节(偏移1-3),使_i对齐到4// _i:偏移4(4字节)// _d:偏移8(8字节,对齐到8)// 总大小:16(最大对齐数8,16是8的整数倍)cout << sizeof(D) << endl; // 输出16
在类的成员函数中,隐含着一个this指针,它指向当前调用函数的对象。this指针的核心作用是区分成员变量和参数(或局部变量)。
class Student {private: int _age; // 成员变量public: void SetAge(int age) { // 参数age与成员变量_age重名 this->_age = age; // this指向当前对象,this->_age即成员变量 } int GetAge() { return this->_age; // 可省略this(编译器自动补充) }};int main() { Student s; s.SetAge(18); // 调用时,this自动指向s cout << s.GetAge() << endl; // 输出18 return 0;}
上述代码中,SetAge函数的参数age与成员变量_age重名,通过this->_age明确访问成员变量。
在 C++ 中,对象的创建和销毁需要特定的函数来管理初始化和清理工作,这就是构造函数和析构函数。它们是面向对象编程中确保对象正确初始化和资源释放的核心机制。
构造函数是一种特殊的成员函数,用于在创建对象时自动完成初始化工作(如分配内存、初始化成员变量等)。
#include <iostream>#include <cstdlib>using namespace std;class Array {private: int* m_data; // 数组数据地址 int m_size; // 数组大小public: // 无参构造函数(函数名与类名相同,无返回值) Array() { cout << "Array的无参构造函数" << endl; m_size = 5; // 初始化大小为5 m_data = (int*)malloc(sizeof(int) * m_size); // 分配内存 } // 其他成员函数(省略) ~Array(); // 析构函数(后续讲解)};// 析构函数定义(释放资源)Array::~Array() { cout << "Array的析构函数" << endl; if (m_data != NULL) { free(m_data); // 释放动态分配的内存 m_data = NULL; }}int main() { Array a1; // 创建对象时,自动调用无参构造函数 return 0;}
运行结果:
Array的无参构造函数Array的析构函数 // 程序结束时自动调用析构函数
和普通函数一样,构造函数支持重载(多个构造函数参数列表不同)。创建对象时,编译器会根据传递的实参匹配对应的构造函数。
class Array {private: int* m_data; int m_size;public: Array(); // 无参构造函数 Array(int s); // 单参数构造函数(指定大小) Array(int s, int init); // 双参数构造函数(指定大小和初始值) ~Array();};// 无参构造函数Array::Array() { cout << "无参构造函数" << endl; m_size = 5; m_data = (int*)malloc(sizeof(int) * m_size);}// 单参数构造函数Array::Array(int s) { cout << "单参数构造函数" << endl; m_size = s; m_data = (int*)malloc(sizeof(int) * m_size);}// 双参数构造函数Array::Array(int s, int init) { cout << "双参数构造函数" << endl; m_size = s; m_data = (int*)malloc(sizeof(int) * m_size); for (int i = 0; i < m_size; i++) { m_data[i] = init; // 初始化数组元素 }}Array::~Array() { free(m_data); m_data = NULL;}int main() { Array a1; // 调用无参构造函数 Array a2(10); // 调用单参数构造函数(括号法) Array a3 = 8; // 调用单参数构造函数(等号法) Array a4(5, 0); // 调用双参数构造函数 Array a5 = (3, 6); // 逗号表达式,实际传递6,调用单参数构造函数 return 0;}
拷贝构造函数是一种特殊的构造函数,用于用已存在的对象初始化新对象。
类名(const 类名& 源对象); // 参数为源对象的常量引用
class Array {private: int* m_data; int m_size;public: Array(); // 无参构造函数 Array(const Array& a); // 拷贝构造函数 ~Array();};Array::Array() { cout << "无参构造函数" << endl; m_size = 5; m_data = (int*)malloc(sizeof(int) * m_size);}// 拷贝构造函数(此处为简化示例,实际需实现深拷贝)Array::Array(const Array& a) { cout << "拷贝构造函数" << endl; m_size = a.m_size; // 复制大小 m_data = (int*)malloc(sizeof(int) * m_size); // 分配新内存 // 后续需复制数据(深拷贝)}Array::~Array() { free(m_data); m_data = NULL;}// 场景2:函数参数为对象(值传递)void print(Array a) { // 调用拷贝构造函数:用实参初始化a // ...}// 场景3:函数返回值为对象Array createArray() { Array temp; return temp; // 调用拷贝构造函数:拷贝temp到返回值}int main() { Array a1; Array a2(a1); // 场景1:用a1初始化a2 print(a1); // 场景2:触发拷贝构造函数 Array a3 = createArray(); // 场景3:触发拷贝构造函数 return 0;}
默认情况下,拷贝构造函数会执行浅拷贝(仅复制指针地址,不复制指针指向的内容),这会导致两个对象共享同一块内存,释放时出现 double free 错误。深拷贝则会复制指针指向的内容,确保每个对象拥有独立的资源。
Array::Array(const Array& a) { // 深拷贝 cout << "深拷贝构造函数" << endl; m_size = a.m_size; // 1. 为新对象分配独立内存 m_data = (int*)malloc(sizeof(int) * m_size); // 2. 复制源对象的数据到新内存 for (int i = 0; i < m_size; i++) { m_data[i] = a.m_data[i]; }}
深拷贝 vs 浅拷贝:
当用户未定义任何构造函数时,编译器会自动生成默认无参构造函数(什么也不做)。但一旦用户定义了构造函数,编译器将不再提供默认版本。
class Demo {public: // 若注释掉以下构造函数,编译器会生成默认无参构造函数 Demo(int a) { // 用户定义了有参构造函数 cout << "有参构造函数" << endl; }};int main() { // Demo d; // 报错:无合适的无参构造函数(编译器未生成) Demo d(10); // 正确:调用用户定义的有参构造函数 return 0;}
析构函数用于在对象销毁时自动完成清理工作(如释放动态内存、关闭文件等)。
class Array {private: int* m_data; int m_size;public: Array(int size) { m_data = (int*)malloc(sizeof(int) * size); // 分配内存 } ~Array() { // 析构函数:释放资源 cout << "释放内存" << endl; if (m_data != NULL) { free(m_data); // 释放动态分配的内存 m_data = NULL; } }};int main() { { Array a(10); // 创建对象,分配内存 } // a离开作用域,自动调用析构函数释放内存 return 0;}
匿名对象是指未命名的临时对象,格式为类名(参数)。其生命周期仅在当前行,执行完后立即销毁(调用析构函数)。
class Test {public: Test() { cout << "构造函数" << endl; } ~Test() { cout << "析构函数" << endl; }};int main() { Test(); // 匿名对象:构造后立即销毁 cout << "----------------" << endl; return 0;}
运行结果:
构造函数析构函数----------------
注意:malloc/free仅分配 / 释放内存,不会调用构造函数 / 析构函数,因此在 C++ 中应优先使用new/delete。
class Test {public: Test() { cout << "无参构造函数" << endl; } Test(int a) { cout << "有参构造函数" << endl; } ~Test() { cout << "析构函数" << endl; }};int main() { // 动态创建对象(调用有参构造函数) Test* pt = new Test(10); // ... 使用对象 delete pt; // 释放对象(调用析构函数) // 动态创建数组(调用无参构造函数) Test* arr = new Test[3]; delete[] arr; // 释放数组(注意加[]) return 0;}
初始化列表用于在构造函数体执行前初始化成员变量,适用于以下场景:
类名(参数) : 成员1(值1), 成员2(值2), ... { // 构造函数体}
class Date {private: int m_year, m_month, m_day;public: // Date没有无参构造函数,必须通过有参构造函数初始化 Date(int y, int m, int d) : m_year(y), m_month(m), m_day(d) { cout << "Date构造函数" << endl; }};class Student {private: const int m_id; // const成员必须初始化 Date birth; // Date对象,无无参构造函数public: // 初始化列表:初始化m_id和birth Student(int id, int y, int m, int d) : m_id(id), birth(y, m, d) { cout << "Student构造函数" << endl; }};int main() { Student s(1001, 2000, 1, 1); // 正确初始化 return 0;}
Date构造函数Student构造函数
在 C++ 中,静态成员(包括静态成员变量和静态成员函数)属于类本身,而非类的某个具体对象。它们是实现多个对象共享数据或功能的重要机制。
静态成员变量被所有类对象共享,不属于某个特定对象,其内存空间在程序启动时分配,生命周期贯穿整个程序。
#include <iostream>using namespace std;class Student {public: static int count; // 静态成员变量:记录学生总数(类内声明) int id; // 普通成员变量:学生编号public: Student() { count++; // 每创建一个对象,总数+1 id = count; // 用总数作为编号 } // 普通成员函数访问静态成员变量 int GetCount() { return count; }};// 静态成员变量必须在类外初始化(定义)int Student::count = 0;int main() { Student s1; Student s2; // 访问静态成员变量的三种方式 cout << s1.count << endl; // 通过对象访问(不推荐) cout << s2.count << endl; // 通过对象访问(不推荐) cout << Student::count << endl; // 通过类名访问(推荐) // 通过普通成员函数访问 cout << s1.GetCount() << endl; // 输出2 return 0;}
2222
静态成员函数属于类本身,用于处理与类相关的操作,不依赖具体对象。
class Student {public: static int count; // 静态成员变量 int id; // 普通成员变量public: Student() { count++; id = count; } // 静态成员函数:只能访问静态成员变量 static int GetCount1() { // return id; // 错误:静态函数不能访问普通成员变量 return count; // 正确:访问静态成员变量 } // 普通成员函数:可访问静态和普通成员变量 int GetId() { return id; }};int Student::count = 0;int main() { Student s1; Student s2; // 通过类名调用静态成员函数(推荐) cout << Student::GetCount1() << endl; // 输出2 // 通过对象调用静态成员函数(不推荐) cout << s1.GetCount1() << endl; // 输出2 // 普通成员函数必须通过对象调用 cout << s1.GetId() << endl; // 输出1 return 0;}
友元(friend)机制允许类外部的函数或其他类访问其私有成员,打破了类的封装性,用于解决特定场景下的访问需求(需谨慎使用,避免过度破坏封装)。
友元函数是在类中声明的外部函数(非成员函数),可以访问类的所有成员(包括私有成员)。
在类内用friend关键字声明函数:
class 类名 { friend 返回类型 函数名(参数); // 声明友元函数};
#include <iostream>using namespace std;class Test { // 声明show为友元函数,允许其访问Test的所有成员 friend void show(Test& t);private: int m_a; // 私有成员public: void set(int a) { m_a = a; // 类内函数可访问私有成员 }};// 友元函数定义(类外)void show(Test& t) { // 友元函数可直接访问Test的私有成员m_a cout << "Test的私有成员m_a = " << t.m_a << endl;}int main() { Test t1; t1.set(100); show(t1); // 调用友元函数,输出:Test的私有成员m_a = 100 return 0;}
若类 B 被声明为类 A 的友元类,则类 B 的所有成员函数都能访问类 A 的所有成员(包括私有成员)。
class A { friend class B; // 声明B为A的友元类};
#include <iostream>using namespace std;class A { friend class B; // 声明B为A的友元类private: int m_a; // A的私有成员public: void set(int a) { m_a = a; }};class B {private: int m_b;public: // B的成员函数可访问A的私有成员 void print(A& a) { cout << "A的私有成员m_a = " << a.m_a << endl; }};int main() { A a1; a1.set(200); B b1; b1.print(a1); // 输出:A的私有成员m_a = 200 return 0;}
相关文章
现在人们打棋牌麻将谁不想赢?手机微乐麻将必赢神器但是手机棋牌麻将是这么好赢的吗?在手机上打棋牌麻将想赢,不仅需要运气,也需要技巧。掌握的棋牌麻将技巧就...
2025-09-09 0
您好:这款游戏可以开挂,确实是有挂的,很多玩家在这款游戏中打牌都会发现很多用户的牌特别好,总是好牌,而且好像能看到-人的牌一样。所以很多小伙伴就怀疑这...
2025-09-09 0
【来源:合肥日报】近日,工业和信息化部、国家药品监督管理局联合公示《生物医用材料创新任务揭榜挂帅(第二批)入围揭榜单位》。合肥启灏医疗科技有限公司(以...
2025-09-09 0
“新修订的科普法于2024年12月25日施行。”“科普是实现创新发展的基础性工作。”……9月4日下午,在一场趣味科学答题互动中,全国科普月海盐主场...
2025-09-09 0
(本文作者陈宏民,上海交通大学行业研究院副院长、上海市政府参事)日前,国务院颁布了《关于深入实施“人工智能+”行动的意见》,为推动我国人工智能与经济社...
2025-09-09 0
自贡融媒记者 池莉 刘晓丹 文/图9月9日,由自贡市科学技术协会和沿滩区人民政府主办,沿滩区科学技术协会与自贡沿滩高新技术产业园区管委会承办的“寻初心...
2025-09-09 0
央视网消息 入秋后,江苏省宿迁市骆马湖水域烟波浩渺,芦苇摇曳。西岸一座60多米高的通信铁塔顶端,白色的监控设备正以每秒数次的频率缓缓旋转,如同一位沉默...
2025-09-09 0
当下,“它经济”爆发,吸引年轻人的广泛关注。某线上平台数据显示,“95后”消费者占比超30%,成宠物消费占比最高用户。不仅如此,“05后”交易用户...
2025-09-09 0
发表评论