C++学习笔记:
C++ 进阶之路__Zwy@的博客-CSDN博客
各位于晏,亦菲们,请点赞关注!
我的个人主页:
_Zwy@-CSDN博客
目录
1、类
1.1、类的定义
1.2、访问限定符
1.2.1、public
1.2.2、private
1.2.3、protected
1.3、struct&&class
1.4、类域
2、对象
2.1、对象的定义—实例化
2.2、类和对象的关系
2.3、对象大小
2.3.1、决定对象大小的因素
2.3.2、内存对齐
2.3.2、对象大小的计算
3、this指针
3.1、this 指针的基本特性
3.2、this指针的使用场景
3.3、this指针的本质
3.4、静态成员函数不含this 指针
3.5、this指针的存储和传递
4、C语言和C++实现stack对比
4.1、C++实现stack
4.2、C语言实现stack
C++知识总结
1、类
1.1、类的定义
在 C++ 中,类 是面向对象编程的核心概念之一。它是一种用户定义的数据类型,用于描述一组具有相同特性(属性)和行为(方法)的对象。
类的定义以class关键字开始,后面跟着类名。类名通常采用大写字母开头的驼峰命名法,类的主体部分包含在一对花括号{}中,在花括号内部可以定义成员变量和成员函数,类中定义的变量叫做成员变量,类中定义的函数叫做成员函数,并且可以使用访问修饰符(private、protected、public)来控制成员的访问权限。最后,类的定义以一个分号;结束。例如实现一个简单的类:
// 定义类
class ClassName {
private:
// 私有成员和函数
int _privateField;
void privateMethod();
protected:
// 受保护成员和函数
int _protectedField;
void protectedMethod();
public:
// 公有成员和函数
int _publicField;
void publicMethod();
};
为了区分成员变量,⼀般来说会在成员变量前加上下划线_,但这并不是C++的语法规定,而是程序员使用的惯例!
定义在类中的成员函数,一般默认为inline内联函数 ,编译器会尝试将这样的函数内联以减少函数调用的开销,但这只是一个建议,编译器可能根据实际情况选择是否执行内联。
class Example {
public:
void inlineMethod() { // 默认是 inline
std::cout << "Inline Method" << std::endl;
}
};
等价于:
inline void Example::inlineMethod() {
std::cout << "Inline Method" << std::endl;
}
1.2、访问限定符
在 C++ 中,访问限定符用于控制类的成员(包括数据成员和成员函数)的可访问性。C++ 有三种访问限定符:public(公有)、private(私有)和protected(保护),这些限定符在类的定义中使用,用于指定类的成员在类外部的可访问程度。
访问权限作用域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为至,如果后面没有访问限定符,作用域就到 }即类结束。
1.2.1、public
public为公有访问限定符,在类定义中,使用public关键字来声明公有成员。可以在类的任何地方以及类的外部访问。例如:
class Example {
public:
int publicVar;
void publicMethod() {
std::cout << "Public Method" << std::endl;
}
};
int main() {
Example obj;
obj.publicVar = 10; // 类外部可以访问
obj.publicMethod(); // 类外部可以访问
return 0;
}
1.2.2、private
private为私有访问限定符,在类定义中,使用private关键字来声明私有成员,只能在类内部访问,类的外部或派生类无法直接访问,通常用于保护类的内部实现细节。例如:
class Example {
private:
int privateVar;
void privateMethod() {
std::cout << "Private Method" << std::endl;
}
};
int main() {
Example obj;
// obj.privateVar = 5; // 错误:无法直接访问 private 成员
return 0;
}
编译器报错信息:
test.cpp: In function ‘int main()’:
test.cpp:8:9: error: ‘int Example::privateVar’ is private
int privateVar;
^
test.cpp:15:10: error: within this context
obj.privateVar = 5; // 错误:无法直接访问 private 成员
1.2.3、protected
protected为保护访问限定符,在类定义中,使用protected关键字来声明保护成员,介于 private 和 public 之间。可以被当前类及其派生类访问,但不能被类外部直接访问。常用于继承,派生类可以直接访问基类中的受保护成员。例如:
class Example {
protected:
int protectedVar;
void protectedMethod() {
cout << "protectedMethod" << endl;
}
};
int main() {
Example obj;
// obj.protectedMethod(); // 错误:类外部无法访问 protected 成员
return 0;
}
编译器报错信息:
test.cpp: In function ‘int main()’:
test.cpp:9:10: error: ‘void Example::protectedMethod()’ is protected
void protectedMethod() {
^
test.cpp:15:25: error: within this context
obj.protectedMethod(); // 错误:类外部无法访问 protected 成员
使用访问限定符的原因和场景:
数据隐藏和封装:通过使用private和protected访问限定符,可以将类的数据成员隐藏起来,只允许通过类提供的公有成员函数来访问和修改数据。这有助于实现数据的封装,防止外部代码随意修改类的内部状态,提高代码的可维护性和安全性。
继承和多态:protected访问限定符在类的继承关系中起到重要作用。它允许派生类访问基类的部分成员,同时又限制了外部对这些成员的访问,有助于实现面向对象编程中的继承和多态特性。注意:一般的成员变量不期望被外界访问,都会修饰为private或者protected ,一般对外提供服务的成员函数都会修饰为public
1.3、struct&&class
C语言中,struct被用来定义结构体,C++兼容C语言的用法,同时C++中,struct还可以用来定义类
实现一个以struct关键字定义的类:
struct Example {
int var; // 默认是 public
};
实现一个以class关键字定义的类:
class Example {
int var; // 默认是 private
};
class和struct定义类的区别:
class定义的类中成员没有被访问限定符修饰时默认为private,而struct定义的类中成员没有被访问限定符修饰时默认为public
1.4、类域
概念:
在 C++ 中,类域(Class Scope)是指类定义所界定的范围。类的成员(包括成员变量和成员函数)都在这个类域内。
类域的范围:
类域指类内部定义的成员的作用范围,包括:
1、数据成员(字段)。
2、成员函数(方法)。
3、嵌套类。
类域内的成员通常通过访问限定符控制访问权限(public、protected、private)。
class MyClass {
int privateVar; // 私有成员:只能在类内部访问
public:
int publicVar; // 公有成员:可以从类外部访问
void method() {
privateVar = 10; // 类内部可以访问 private 成员
publicVar = 20; // 类内部可以访问 public 成员
}
};
int main() {
MyClass obj;
obj.publicVar = 5; // 可以访问 public 成员
// obj.privateVar = 5; // 错误:无法访问 private 成员
return 0;
}
访问控制与作用域规则:
在类中,作用域和访问控制由访问限定符决定:
访问限定符 | 类内部访问 | 派生类访问 | 类外部访问 |
---|---|---|---|
private | 可以 | 不可以 | 不可以 |
protected | 可以 | 可以 | 不可以 |
public | 可以 | 可以 | 可以 |
静态成员与类域:
静态成员属于类本身,而不是类的实例,必须在类外初始化。
class MyClass {
public:
static int staticVar; // 静态成员声明
static void staticMethod() {
std::cout << "Static Method: " << staticVar << std::endl;
}
};
// 静态成员的初始化
int MyClass::staticVar = 0;
int main() {
MyClass::staticVar = 10; // 通过类名访问
MyClass::staticMethod(); // 调用静态方法
return 0;
}
嵌套类与作用域:
嵌套类的作用域局限于外部类,但可以访问外部类的 private
成员(如果外部类提供了访问机制)。
class Outer {
private:
int privateVar;
public:
class Inner {
public:
void accessOuter(Outer& o) {
o.privateVar = 10; // 访问外部类的 private 成员
}
};
};
int main() {
Outer outer;
Outer::Inner inner;
inner.accessOuter(outer);
return 0;
}
命名空间与类域:
类域的定义通常嵌套在命名空间中,用于防止名称冲突。
namespace _Zwy {
class MyClass {
public:
void method() {
std::cout << "Inside MyNamespace::MyClass" << std::endl;
}
};
}
int main() {
_Zwy::MyClass obj;
obj.method();
return 0;
}
2、对象
对象是面向对象编程(OOP)中的一个核心概念,是一个类的实例。对象将数据和操作(行为)结合在一起,表示真实世界中的某个具体实体或抽象概念。类的实例化创建出来的就是对象!
2.1、对象的定义—实例化
类是模板,对象是实例:类定义了某种类型的结构和行为,而对象是按照类的模板创建出来的实际实例。
对象的组成:属性(数据成员):对象的状态(由类中的字段定义)。
行为(成员函数):对象的操作能力(由类中的方法定义)。
class Car {
public:
string brand; // 属性:品牌
int speed; // 属性:速度
void drive() { // 行为:驾驶
std::cout << brand << " is driving at " << speed << " km/h." << std::endl;
}
};
int main() {
Car car; // 创建对象 car1
car.brand = "Toyota"; // 设置属性
car.speed = 100; // 设置属性
car.drive(); // 调用行为
return 0;
}
特点:
唯一性:每个对象在内存中是独立存在的,具有唯一的地址。
封装性:对象隐藏其内部实现,通过方法(行为)与外界交互。
2.2、类和对象的关系
类是抽象的,定义了一种类型的结构和行为。对象是具体的,实例化类后在内存中创建。
class Animal {
public:
string name;
int age;
void eat() {
std::cout << name << " is eating." << std::endl;
}
};
int main() {
Animal dog; // 对象:dog
dog.name = "Buddy";
dog.age = 3;
dog.eat(); // 调用方法
return 0;
}
多个对象可以由同一个类创建,每个对象都有自己的属性值。
class Rectangle {
public:
int width, height;
int area() {
return width * height;
}
};
int main() {
Rectangle rect1, rect2;
rect1.width = 5; rect1.height = 10;
rect2.width = 3; rect2.height = 6;
std::cout << "Area of rect1: " << rect1.area() << std::endl;
std::cout << "Area of rect2: " << rect2.area() << std::endl;
return 0;
}
输出:
Area of rect1: 50
Area of rect2: 18
2.3、对象大小
在C++中,对象的大小是一个与内存管理息息相关的重要概念。作为类的实例,对象不仅封装了数据,还反映了程序运行时的内存占用情况。对象的大小并不仅仅是简单地将类中非静态成员变量的大小相加,还受到内存对齐、填充字节以及类中是否包含虚函数等因素的影响
2.3.1、决定对象大小的因素
对象的大小只包括类中非静态数据成员(成员变量)所占的内存空间:
静态数据成员、成员函数以及虚函数的代码(包括它们的地址)都不包含在对象本身的大小中
成员函数:
成员函数在编译阶段会被编译成代码段中的独立函数,所有实例共享一份函数代码。因此,成员函数并不占用每个对象的存储空间。
虚函数表指针(对于多态类):
如果类包含虚函数,编译器通常会在对象中存储一个指向虚函数表(vtable)的指针,用于实现运行时多态。
这种情况下,对象的大小会增加一个指针的大小(通常是 4 字节或 8 字节,取决于平台)。
静态成员变量:
静态成员变量属于类而不属于某个对象,因此也不包含在对象大小中。
内存对齐:
编译器会为数据成员添加填充字节,以确保数据对齐符合系统要求。
对象大小的计算涉及到内存对齐,我们先来理解什么是内存对齐?
2.3.2、内存对齐
为什么需要内存对齐?
CPU 从内存读取数据时,并不是逐字节读取,而是按一定的 字节块(如 4 字节或 8 字节) 读取。如果数据不对齐,CPU 需要执行额外的操作来读取数据,影响性能。
内存对齐的规则:
第⼀个成员在与结构体偏移量为0的地址处。
其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的⼀个对齐数与该成员大小的较小值。
VS中默认的对齐数为8
结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
示例:
struct Example {
char a; // 1 字节
int b; // 4 字节
short c; // 2 字节
};
int main() {
std::cout << "Size of Example: " << sizeof(Example) << std::endl;
return 0;
}
输出:
Size of Example: 12
内存布局:
成员 | 字节占用 | 备注 |
---|---|---|
char a | 1 | 1 字节 |
填充 | 3 | 填充到 4 字节对齐 |
int b | 4 | 4 字节对齐 |
short c | 2 | 2 字节 |
填充 | 2 | 填充到 4 字节整数倍 |
总大小:1 + 3 + 4 + 2 + 2 = 12 字节
调整成员顺序减少填充:
struct Optimized {
char a; // 1 字节
short c; // 2 字节
int b; // 4 字节
};
int main() {
std::cout << "Size of Optimized: " << sizeof(Optimized) << std::endl;
return 0;
}
输出:
Size of Optimized: 8
优化后的内存布局:
成员 | 字节占用 | 备注 |
---|---|---|
char a | 1 | 1 字节 |
short c | 2 | 紧接着占 2 字节 |
填充 | 1 | 填充到 4 字节对齐 |
int b | 4 | 4 字节对齐 |
总大小:1 + 2 + 1 + 4 = 8 字节
通过调整成员的顺序,结构体的大小从 12 字节 减少到了 8 字节。
嵌套结构体的内存对齐:
嵌套结构体的内存对齐规则
嵌套结构体的每个成员仍然遵循自身的对齐规则。
外部结构体会将嵌套结构体视为一个整体,嵌套结构体的大小和对齐要求会影响外部结构体的内存布局。
外部结构体的大小必须是所有成员的最大对齐系数的整数倍,包括嵌套结构体的对齐。
简单的嵌套结构体:
struct Inner {
char a; // 1 字节
int b; // 4 字节
};
struct Outer {
char x; // 1 字节
Inner inner; // 嵌套结构体
short y; // 2 字节
};
int main() {
std::cout << "Size of Inner: " << sizeof(Inner) << " bytes" << std::endl;
std::cout << "Size of Outer: " << sizeof(Outer) << " bytes" << std::endl;
return 0;
}
输出:
Size of Inner: 8 bytes
Size of Outer: 16 bytes
内存布局分析:
成员 | 字节偏移量 | 占用字节 | 说明 |
---|---|---|---|
char x | 0 | 1 | 成员 x 占 1 字节 |
填充 | 1 | 3 | 填充 3 字节,使下一个成员按 4 字节对齐 |
Inner (嵌套结构体) | 4 | 8 | Inner 的大小为 8 字节(按 4 字节对齐) |
short y | 12 | 2 | 成员 y 占 2 字节 |
填充 | 14 | 2 | 末尾填充,使结构体总大小按 4 字节对齐 |
总大小:1 + 3 + 8 + 2 + 2 = 16 字节
优化后的结构体(成员顺序调整)
struct OuterOptimized {
char x; // 1 字节
short y; // 2 字节
Inner inner; // 嵌套结构体
};
内存布局分析:
成员 | 字节偏移量 | 占用字节 | 说明 |
---|---|---|---|
char x | 0 | 1 | 成员 x 占 1 字节 |
short y | 2 | 2 | 成员 y 占 2 字节 |
填充 | 4 | 4 | 填充 4 字节,使下一个成员按 8 字节对齐 |
Inner (嵌套结构体) | 8 | 8 | Inner 的大小为 8 字节(按 4 字节对齐) |
总大小:1 + 2 + 1 + 8 = 12 字节
,按最大对齐 4 字节,最终为 16 字节。
多层嵌套结构体:
struct Level1 {
char a; // 1 字节
int b; // 4 字节
};
struct Level2 {
Level1 l1; // 8 字节
double d; // 8 字节
};
struct Level3 {
char x; // 1 字节
Level2 l2; // 16 字节
};
内存布局分析:
成员 | 字节偏移量 | 占用字节 | 说明 |
---|---|---|---|
char x | 0 | 1 | 成员 x 占 1 字节 |
填充 | 1 | 7 | 填充 7 字节,使下一个成员按 8 字节对齐 |
Level2 l2 | 8 | 16 | 嵌套结构体 Level2 的大小为 16 字节 |
总大小:1 + 7 + 16 = 24 字节
自定义对齐规则
C++ 提供了 #pragma pack
指令和 alignas
关键字,允许我们自定义内存对齐方式。
使用 #pragma pack
控制对齐
#pragma pack(1) // 设置对齐字节为 1
struct Example {
char a;
int b;
short c;
};
#pragma pack() // 恢复默认对齐
int main() {
std::cout << "Size of Example: " << sizeof(Example) << std::endl;
return 0;
}
#pragma pack(1)
表示按 1 字节对齐,不会添加填充字节,但可能降低性能。
输出:
Size of Example: 7
使用 alignas
控制对齐
struct Example {
alignas(8) char a; // 强制 8 字节对齐
int b;
};
int main() {
std::cout << "Size of Example: " << sizeof(Example) << std::endl;
return 0;
}
输出:
Size of Example: 8
2.3.2、对象大小的计算
仅包含非静态数据成员:
class MyClass {
int a; // 4 bytes
char b; // 1 byte (but padded to 4 bytes for alignment)
double c; // 8 bytes
};
int main() {
std::cout << "Size of MyClass: " << sizeof(MyClass) << " bytes" << std::endl;
return 0;
}
输出:
Size of MyClass: 16 bytes
包含静态成员:
class MyClass {
int a; // 4 bytes
static int b; // 静态成员,不计入对象大小
};
int main() {
std::cout << "Size of MyClass: " << sizeof(MyClass) << " bytes" << std::endl;
return 0;
}
输出: 4
字节(只包括 int a
的大小)
Size of MyClass: 4 bytes
空类:
class Empty {};
int main() {
std::cout << "Size of Empty: " << sizeof(Empty) << " bytes" << std::endl;
return 0;
}
输出:C++ 要求每个对象在内存中有唯一地址,因此即使空类的大小为 1 字节。
Size of Empty: 1 bytes
完整示例:
class Test {
int a; // 非静态数据成员
static int b; // 静态数据成员
void func() {} // 成员函数
virtual void vfunc() {} // 虚函数
};
int main() {
std::cout << "Size of Test: " << sizeof(Test) << " bytes" << std::endl;
return 0;
}
输出:sizeof(Test) 只包括非静态数据成员a的大小和虚函数表指针大小,成员函数 func() 和虚函数代码不影响对象大小,它们是共享的代码段。
Size of Test: 8 bytes
内存对齐的影响:
class MyClass1 {
char a; // 1 byte
int b; // 4 bytes
char c; // 1 byte
};
class MyClass2 {
char a; // 1 byte
char c; // 1 byte
int b; // 4 bytes
};
int main() {
std::cout << "Size of MyClass1: " << sizeof(MyClass1) << " bytes" << std::endl;
std::cout << "Size of MyClass2: " << sizeof(MyClass2) << " bytes" << std::endl;
return 0;
}
输出:
Size of MyClass1: 12 bytes
Size of MyClass2: 8 bytes
虚函数的影响:(继承和多态会讲到)
如果类中包含虚函数,每个对象会多出一个指针(通常是 4 或 8 字节,取决于系统架构),指向虚函数表(V-Table)。
输出: int a 占 4 字节。虚函数表指针占 4 字节(在 32 位系统)或 8 字节(在 64 位系统)
Size of MyClass: 8 bytes
3、this指针
在 C++ 中,this 是一个特殊的指针,指向当前对象本身。它是非静态成员函数的一个隐式参数,只有在调用类的非静态成员函数时,this 指针才会被传递给函数。简单来说,this 指针表示调用该函数的具体对象的地址。
3.1、this 指针的基本特性
this 是一个指针:指向当前对象的内存地址。
隐式传递:this 指针是由编译器自动提供的,无需显式传递。
只能用于非静态成员函数:静态成员函数不属于任何具体对象,因此没有 this 指针。
常量性:this 指针本身是不可修改的,它只能指向当前对象。
3.2、this指针的使用场景
区分成员变量和局部变量/参数:
当成员变量与函数参数或局部变量同名时,可以使用 this
指针来明确指向成员变量。
class MyClass {
private:
int x;
public:
void setX(int x) { // 参数和成员变量同名
this->x = x; // 使用 this 指针区分成员变量和参数
}
void display() {
std::cout << "x = " << this->x << std::endl;
}
};
int main() {
MyClass obj;
obj.setX(10); // 设置成员变量 x
obj.display(); // 输出:x = 10
return 0;
}
输出: 在 setX
函数中,this->x
明确表示成员变量 x
,而参数 x
覆盖了同名的成员变量。
x = 10
返回当前对象自身(实现链式调用):
this
指针可以返回当前对象的引用,实现链式调用。
class MyClass {
private:
int value;
public:
MyClass& setValue(int v) { // 返回当前对象的引用
this->value = v;
return *this; // *this 解引用指向当前对象
}
void display() {
std::cout << "Value = " << value << std::endl;
}
};
int main() {
MyClass obj;
obj.setValue(10).setValue(20).display(); // 链式调用
return 0;
}
输出:setValue
返回 *this
(当前对象的引用),使调用者可以连续调用其他方法。
Value = 20
3.3、this指针的本质
在底层实现中,this
指针是通过编译器隐式传递给非静态成员函数的
class MyClass {
public:
void func() {
std::cout << "Function called" << std::endl;
}
};
int main() {
MyClass obj;
obj.func();
return 0;
}
如上代码,实际上被编译器转换为:(不能在实参和形参的位置显示的写this指针(编译时编译器会处理),但是可以在函数体内显示使用this指针)
void MyClass::func(MyClass* this) { // 编译器自动传递 this 指针
std::cout << "Function called" << std::endl;
}
int main() {
MyClass obj;
MyClass::func(&obj); // 隐式传递 this 指针
return 0;
}
编译器会自动将 this 指针传递给成员函数,因此在函数内部可以访问调用对象的成员。
3.4、静态成员函数不含this
指针
静态成员函数属于类本身,而不属于某个具体对象。因此,它们不需要 this
指针。
class MyClass {
public:
static void staticFunc() {
// std::cout << this; // 错误:静态函数没有 this 指针
std::cout << "Static function called." << std::endl;
}
};
int main() {
MyClass::staticFunc();
return 0;
}
输出:
Static function called.
静态函数无法访问非静态数据成员,因为它们不依赖于具体对象,没有this指针
3.5、this指针的存储和传递
this 指针的存储位置:
this 指针并不是一个变量,而是由编译器在函数调用时隐式传递给非静态成员函数的一个指针。在对象的内存中,this 指针并不占据实际空间,它只是一个调用函数时的临时指针。
在函数调用栈上,this 指针作为一个隐藏参数存储于栈内,具体存储位置取决于编译器实现与调用约定
this 指针的传递机制:
在调用非静态成员函数时,编译器会将调用对象的地址传递给该函数,生成的机器代码会将对象地址存储到 this 指针中。
一般来说,this 指针存储在寄存器中(如 ECX 寄存器,在 x86 的 cdecl 调用约定 下),也可能存储在栈内存中,具体取决于平台和编译器
this
指针的隐式传递:
class MyClass {
public:
void display() {
std::cout << "Address of this: " << this << std::endl;
}
};
int main() {
MyClass obj;
std::cout << "Address of obj: " << &obj << std::endl;
obj.display();
return 0;
}
输出:在调用 obj.display() 时,this 指针被隐式传递给 display() 函数。在函数内部,this 指向当前对象 obj 的地址。4\
Address of obj: 00A5FBAF
Address of this: 00A5FBAF
this 指针存储的具体过程:
调用栈分配:
当调用非静态成员函数时,调用对象的地址被压入栈,作为 this 指针传递给函数.
寄存器优化:
在某些平台上,为了优化性能,this 指针会存储在特定的寄存器中(例如 x86 平台的 ECX 寄存器)。
编译器生成代码:
编译器会在函数调用时,将对象地址传递给函数中的 this 指针,确保其在函数内部可用。
汇编层面理解:
假设使用 x86 架构,编译器生成的汇编代码会将 this
指针存储在 ECX
寄存器中:
C++:
class MyClass {
public:
void func() {
std::cout << "Function called" << std::endl;
}
};
int main() {
MyClass obj;
obj.func();
return 0;
}
汇编:(简化)
; obj.func() 的调用过程
mov ecx, OFFSET obj ; 将 obj 的地址存储到 ECX(this 指针)
call MyClass::func ; 调用 func,this 指针已传入
在调用 func() 之前,编译器会将 obj 的地址存储到寄存器 ECX。当 func 执行时,this 指针指向 obj。
4、C语言和C++实现stack对比
面向对象编程(Object-Oriented Programming, OOP)的三大特点是封装、继承和多态。这三个特性是 OOP 的核心思想,它们通过抽象、模块化和代码复用性,使得程序更易于设计、维护和扩展。通过下面的对比,我们可以先感受一下封装的魅力!
封装的本质:
封装是面向对象编程(OOP)的核心特性之一,强调将数据和操作封装在一起,通过控制访问权限实现信息隐藏和接口抽象。封装的本质在于保护对象的内部状态,同时为外部提供受控的访问接口。
核心点:
信息隐藏:类的内部细节对外部不可见。
访问控制:通过访问修饰符(private, protected, public)限制外部对类成员的访问。
对外提供接口:通过方法访问或修改内部数据,保证对象状态的完整性和安全性。
C++中数据和函数都封装到了类里面,通过访问限定符进行了、限制,不能再随意通过对象直接访问数据,这是C++封装的⼀种体现,这个是最重要的变化。这里的封装的本质是⼀种更严格规范的管理,避免出现乱访问修改的问题。当然封装不仅仅是这样的,我们后面还需要不断的去习。
4.1、C++实现stack
#include<iostream>
#include<assert.h>
using namespace std;
typedef int STDataType;
class Stack
{
public:
// 成员函数
void Init(int n = 4)
{
_a = (STDataType*)malloc(sizeof(STDataType) * n);
if (nullptr == _a)
{
perror("malloc failed");
return;
}
_capacity = n;
_top = 0;
}
void Push(STDataType x)
{
if (_top == _capacity)
{
int newcapacity = _capacity * 2;
STDataType* tmp = (STDataType*)realloc(_a, newcapacity *
sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc failed");
return;
}
_a = tmp;
_capacity = newcapacity;
}
_a[_top++] = x;
}
void Pop()
{
assert(_top > 0);
--_top;
}
bool Empty()
{
return _top == 0;
}
int Top()
{
assert(_top > 0);
return _a[_top - 1];
}
void Destroy()
{
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:// 成员变量
STDataType* _a;
size_t _capacity;
size_t _top;
};
4.2、C语言实现stack
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
void STInit(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
void STDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
void STPush(ST* ps, STDataType x)
{
assert(ps);// 满了, 扩容
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity *
sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
return;
}
ps->a = tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
bool STEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
void STPop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));
ps->top--;
}
STDataType STTop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));
return ps->a[ps->top - 1];
}
int STSize(ST* ps)
{
assert(ps);
return ps->top;
}
C 语言:
没有类和对象的概念,只能通过结构体struct和函数的组合来实现栈。
数据和操作是分开的,封装性较差。
需要显式传递结构体指针,操作数据。
C++:
提供类(class) 和 对象 的概念,将数据和行为封装在一起。
通过成员函数操作栈,具有更好的封装性和安全性。
构造函数和析构函数自动管理内存(new/delete)
C++知识总结
1、类:自定义类型,访问限定符控权限,struct 与 class 有别,有类域概念。
2、对象:类实例化产物,大小受成员变量及内存对齐影响,体现类的具体化。
3、this 指针:非静态成员函数里指向当前对象,用于区分变量,静态函数无,本质特殊且有存储传递方式。
4、C 与 C++ 实现 stack:C++ 靠类封装,清晰安全;C 语言用结构体与函数指针,灵活但手动管理内存且耦合度高。在 C++ 中,类与对象是根基。它们奠定了 C++ 世界的基石,是开启编程进阶之路的必经之门,本文主要讲解最基本的类和对象的定义,在进阶部分会对类和对象进行更深层次的剖析,为后续C++飞升之路打造最有力的制胜法宝!
如上的讲解只是我的一些拙见,如有不足之处,还望各位大佬不吝在评论区予以斧正,感激不尽!创作不易,还请多多互三支持!你们的支持是我最大的动力!