【C++ 类和对象 基础篇】—— 抽象思维的巅峰舞者,演绎代码的深邃华尔兹

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 a11 字节
填充3填充到 4 字节对齐
int b44 字节对齐
short c22 字节
填充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 a11 字节
short c2紧接着占 2 字节
填充1填充到 4 字节对齐
int b44 字节对齐

总大小: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 x01成员 x 占 1 字节
填充13填充 3 字节,使下一个成员按 4 字节对齐
Inner(嵌套结构体)48Inner 的大小为 8 字节(按 4 字节对齐)
short y122成员 y 占 2 字节
填充142末尾填充,使结构体总大小按 4 字节对齐

总大小:1 + 3 + 8 + 2 + 2 = 16 字节


优化后的结构体(成员顺序调整) 

struct OuterOptimized {
    char x;       // 1 字节
    short y;      // 2 字节
    Inner inner;  // 嵌套结构体
};

内存布局分析: 

成员字节偏移量占用字节说明
char x01成员 x 占 1 字节
short y22成员 y 占 2 字节
填充44填充 4 字节,使下一个成员按 8 字节对齐
Inner(嵌套结构体)88Inner 的大小为 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 x01成员 x 占 1 字节
填充17填充 7 字节,使下一个成员按 8 字节对齐
Level2 l2816嵌套结构体 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++飞升之路打造最有力的制胜法宝!

如上的讲解只是我的一些拙见,如有不足之处,还望各位大佬不吝在评论区予以斧正,感激不尽!创作不易,还请多多互三支持!你们的支持是我最大的动力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/942300.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

(高可用版本)Kubeadm+Containerd+keepalived部署高可用k8s(v1.28.2)集群

KubeadmContainerdkeepalived部署高可用k8s(v1.28.2)集群 一.环境准备&#xff0c;二.容器运行时Containerd安装请参照前文。KubeadmContainerd部署k8s(v1.28.2)集群&#xff08;非高可用版&#xff09;-CSDN博客 文章目录 KubeadmContainerdkeepalived部署高可用k8s(v1.28.2)集…

联合目标检测与图像分类提升数据不平衡场景下的准确率

联合目标检测与图像分类提升数据不平衡场景下的准确率 在一些数据不平衡的场景下&#xff0c;使用单一的目标检测模型很难达到99%的准确率。为了优化这一问题&#xff0c;适当将其拆解为目标检测模型和图像分类模型的组合&#xff0c;可以更有效地控制最终效果&#xff0c;尤其…

C++之红黑树模拟实现

目录 红黑树的概念 红黑树的性质 红黑树的查找效率 红黑树的实现 红黑树的定义 红黑树节点的插入 红黑树的平衡调整 判断红黑树是否平衡 红黑树整体代码 测试代码 上期我们学习了AVL树的模拟实现&#xff0c;在此基础上&#xff0c;我们本期将学习另一个数据结构-…

机器学习常用术语

目录 概要 机器学习常用术语 1、模型 2、数据集 3、样本与特征 4、向量 5、矩阵 6、假设函数与损失函数 7、拟合、过拟合与欠拟合 8、激活函数(Activation Function) 9、反向传播(Backpropagation) 10、基线(Baseline) 11、批量(Batch) 12、批量大小(Batch Size)…

nest 学习3

学习小册(nest通关秘籍) 邮箱验证码登陆 流程图&#xff1a; 邮箱作为key&#xff0c;生成随机验证码&#xff0c;然后放到redis中。调用邮箱api发送邮箱。 前端获取到code后&#xff0c;将验证码输入传给后端&#xff0c;后端根据邮箱取出redis数据&#xff0c;比对验证码&…

原点安全再次入选信通院 2024 大数据“星河”案例

近日&#xff0c;中国信息通信研究院和中国通信标准化协会大数据技术标准推进委员会&#xff08;CCSA TC601&#xff09;共同组织开展的 2024 大数据“星河&#xff08;Galaxy&#xff09;”案例征集活动结果正式公布。由工银瑞信基金管理有限公司、北京原点数安科技有限公司联…

RabbitMQ 的7种工作模式

RabbitMQ 共提供了7种⼯作模式,进⾏消息传递,. 官⽅⽂档:RabbitMQ Tutorials | RabbitMQ 1.Simple(简单模式) P:⽣产者,也就是要发送消息的程序 C:消费者,消息的接收者 Queue:消息队列,图中⻩⾊背景部分.类似⼀个邮箱,可以缓存消息;⽣产者向其中投递消息,消费者从其中取出消息…

Restaurants WebAPI(四)——Identity

文章目录 项目地址一、Authentication&#xff08;身份认证&#xff09;1.1 配置环境(解决类库包无法引用)1.2 使用Authentication控制Controller的访问1.3 获取User的Context1.3.1 在Application下创建User文件夹1. 创建User.cs record类封装角色信息2. 创建UserContext.cs提供…

010 Qt_输入类控件(LineEdit、TextEdit、ComboBox、SpinBox、DateTimeEdit、Dial、Slider)

文章目录 前言一、QLineEdit1.简介2.常见属性及说明3.重要信号及说明4.示例一&#xff1a;用户登录界面5.示例二&#xff1a;验证两次输入的密码是否一致显示密码 二、TextEdit1.简介2.常见属性及说明3.重要信号及说明4.示例一&#xff1a;获取多行输入框的内容5.示例二&#x…

Vue3:uv-upload图片上传

效果图&#xff1a; 参考文档&#xff1a; Upload 上传 | 我的资料管理-uv-ui 是全面兼容vue32、nvue、app、h5、小程序等多端的uni-app生态框架 (uvui.cn) 代码&#xff1a; <view class"greenBtn_zw2" click"handleAddGroup">添加班级群</vie…

通过Docker Compose来实现项目可以指定读取不同环境的yml包

通过Docker Compose来实现项目可以指定读取不同环境的yml包 1. 配置文件2. 启动命令 切换不同环境注意挂载的文件权限要777 1. 配置文件 version: 3.8 services:docker-test:image: openjdk:8-jdk-alpineports:- "${APP_PORT}:${CONTAINER_PORT}"volumes:- "${J…

华为实训课笔记 2024 1223-1224

华为实训 12/2312/24 12/23 [Huawei]stp enable --开启STP display stp brief --查询STP MSTID Port Role STP State Protection 实例ID 端口 端口角色 端口状态 是否开启保护[Huawei]display stp vlan xxxx --查询制定vlan的生成树计算结…

GitCode 光引计划投稿 | GoIoT:开源分布式物联网开发平台

GoIoT 是基于Gin 的开源分布式物联网&#xff08;IoT&#xff09;开发平台&#xff0c;用于快速开发&#xff0c;部署物联设备接入项目&#xff0c;是一套涵盖数据生产、数据使用和数据展示的解决方案。 GoIoT 开发平台&#xff0c;它是一个企业级物联网平台解决方案&#xff…

EasyGBS国标GB28181公网平台P2P远程访问故障诊断:云端服务端排查指南

随着信息技术的飞速发展&#xff0c;视频监控领域正经历从传统安防向智能化、网络化安防的深刻转变。EasyGBS平台&#xff0c;作为基于国标GB28181协议的视频流媒体平台&#xff0c;为用户提供了强大的视频监控直播功能。然而&#xff0c;在实际应用中&#xff0c;P2P远程访问可…

Vnlhun靶场Log4j2漏洞

相关概念 log4j2是Apache的⼀个java日志框架&#xff0c;我们借助它进行日志相关操作管理&#xff0c;然而在2021年末log4j2爆出了远程代码执行漏洞&#xff0c;属于严重等级的漏洞 漏洞原理 简单说就是当你使⽤log4j2中提供的⽅法去输出⽇志信息时&#xff0c;⽐如说最常⻅…

千兆网中的gmii与rgmii

物理链路上是千兆网。1 Gbps1000 Mb/s1000/8 MB/s125 MB/s&#xff0c;这是和你的测试设备相连的1 Gbps物理带宽下的极速。关键点是1 B&#xff08;byte&#xff09;8 b&#xff08;bit&#xff09;。实际下载速度还取决于下载源的限制、出口的物理链路和运营商的限制。

2024-12-24 NO1. XR Interaction ToolKit 环境配置

文章目录 1 软件配置2 安装 XRToolKit3 配置 OpenXR4 安装示例场景5 运行测试 1 软件配置 Unity 版本&#xff1a;Unity6000.0.26 ​ 2 安装 XRToolKit 创建新项目&#xff08;URP 3D&#xff09;&#xff0c;点击进入 Asset Store。 进入“Unity Registry”页签&#xff0…

重温设计模式--外观模式

文章目录 外观模式&#xff08;Facade Pattern&#xff09;概述定义 外观模式UML图作用 外观模式的结构C 代码示例1C代码示例2总结 外观模式&#xff08;Facade Pattern&#xff09;概述 定义 外观模式是一种结构型设计模式&#xff0c;它为子系统中的一组接口提供了一个统一…

【恶意软件检测】一种基于API语义提取的Android恶意软件检测方法(期刊等级:CCF-B、Q2)

一种基于API语义提取的Android恶意软件检测方法 A novel Android malware detection method with API semantics extraction 摘要 由于Android框架和恶意软件的持续演变&#xff0c;使用过时应用程序训练的传统恶意软件检测方法在有效识别复杂演化的恶意软件方面已显不足。为…

【微信小程序】2|轮播图 | 我的咖啡店-综合实训

轮播图 引言 在微信小程序中&#xff0c;轮播图是一种常见的用户界面元素&#xff0c;用于展示广告、产品图片等。本文将通过“我的咖啡店”小程序的轮播图实现&#xff0c;详细介绍如何在微信小程序中创建和管理轮播图。 轮播图数据准备 首先&#xff0c;在home.js文件中&a…