目录
前言
1.类的定义
1.1类定义的格式
1.2访问限定符
1.3类域
2.实例化
2.1实例化概念
2.2对象大小
3.this指针
4.选择题补充练习
结束语

Hello,友友们,好久不见啦,前面将C++的基本知识了解了一下,今天将学习C++的另一知识,类与对象的学习,学习起来稍有难度哦!!!
类(Class)
类是一个抽象的概念,它定义了一组具有相同属性(attribute)和方法(method)的对象的模板。类为创建对象提供了蓝图。在类中,你可以定义以下内容:
- 属性(Attributes):变量,用于存储数据。属性可以是类的属性(所有对象共享)或实例属性(每个对象有自己的副本)。
- 方法(Methods):函数,用于定义可执行的操作。方法通常与类的属性交互。
• class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。• 为了区分成员变量,一般习惯上成员变量会加一个特殊标识,如成员变量前面或者后面加_ 或者 m开头,注意C++中这个并不是强制的,只是一些惯例。
#include using namespace std; class Date{ public: void print(int year, int month, int day) { _year = year; _month = month; _day = day; cout << _year << " " << _month << " " << _day << endl; //cout << year << " " << month << " " << day << endl; } private: // 为了区分成员变量,⼀般习惯上成员变量 // 会加⼀个特殊标识,如_ 或者 m开头 int _year; // year_ m_year int _month; int _day; }; int main() { Date date; date.print(2024, 7, 18); return 0; } 

#include using namespace std; // C++升级struct升级成了类 // 1、类⾥⾯可以定义函数 // 2、struct名称就可以代表类型 // C++兼容C中struct的⽤法 typedef struct ListNodeC { struct ListNodeC* next; int val; }LTNode; // 不再需要typedef,ListNodeCPP就可以代表类型 struct ListNodeCPP { void Init(int x) { next = nullptr; val = x; } ListNodeCPP* next; int val; }; int main() { return 0; } • 定义在类面的成员函数默认为inline。 • C++一种实现封装的方式,用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。• public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访问,protected和private是一样的,以后继承章节才能体现出他们的区别。• 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止,如果后面没有访问限定符,作用域就到 }即类结束。• class定义成员没有被访问限定符修饰时默认为private,struct默认为public。• 一般成员变量都会被限制为private/protected,需要给别人使用的成员函数会放为public。
在C++中,每个类定义了自己的作用域。这意味着类的成员(包括数据成员和成员函数)都存在于类的作用域内。当你想要在类的外部引用类的成员时,你需要使用类的作用域解析运算符 `::` 来指明特定的成员属于哪个类。
1.3.1类定义一个作用域
当你定义一个类时,你就创建了一个新的作用域。在这个作用域内,你可以定义变量(数据成员)和函数(成员函数)。
1.3.2类成员在类的作用域中
所有在类定义内部声明的成员都属于该类的作用域。例如:
class MyClass { public: void myFunction(); int myValue; };
在这里,myFunction 和 myValue都是在 MyClass 的作用域中。
1.3.3在类体外定义成员
当需要在类的外部定义成员函数时,需要使用类名和作用域解析运算符 `::` 来指定该函数属于哪个类的作用域。
class MyClass { public: void myFunction(); // 成员函数声明 int myValue; // 数据成员声明 }; // 在类体外定义成员函数 void MyClass::myFunction() { // 函数实现 myValue = 10; // 直接使用类成员 } 在上面的例子中,myFunction 的定义在类 MyClass 的外部。为了指明 myFunction是 MyClass 的成员,我们使用了 MyClass::。
MyClass obj; obj.myFunction(); // 调用成员函数 int value = obj.myValue; // 访问数据成员 
#include using namespace std; class Date { public: void Init(int year, int month, int day) { _year = year; _month = month; _day = day; } void Print() { cout << _year << "/" << _month << "/" << _day << endl; } private: // 这⾥只是声明,没有开空间 int _year; int _month; int _day; }; int main() { // Date类实例化出对象d1和d2 Date d1; Date d2; d1.Init(2024, 7, 18); d1.Print(); d2.Init(2024, 7, 19); d2.Print(); return 0; 
上面分析了对象中只存储成员变量,C++规定类实例化的对象也要符合内存对齐的规则。与结构体内存对齐计算一样。1. 默认对齐方式:每个数据类型的自然对齐方式通常是该类型的大小。例如,`int` 类型通常在4字节边界上对齐,`double` 类型通常在8字节边界上对齐。2. 结构体和类的对齐:结构体和类的成员按照其类型的自然对齐方式对齐。结构体或类的整体大小也必须符合其最大成员类型的对齐要求。3. 成员对齐:结构体或类的成员按照声明顺序进行排列,每个成员按其类型的对齐方式放置。4. 填充(Padding):为了满足对齐要求,编译器可能会在成员之间插入填充字节。5. #pragma pack:可以通过`#pragma pack`指令来改变或设置编译器的默认对齐方式。
下面是一个例子,展示了一个结构体和一个类的对齐情况: struct MyStruct { char a; // 1 byte int b; // 4 bytes char c; // 1 byte }; class MyClass { public: char a; // 1 byte int b; // 4 bytes char c; // 1 byte }; // 假设默认对齐方式为4字节
在这个例子中,`MyStruct`和`MyClass`的对齐方式是一样的。成员`a`和`c`之间会有3个填充字节,以确保`b`在4字节边界上对齐。因此,`MyStruct`和`MyClass`的大小将是12字节,而不是简单的7字节(1+4+1+3填充字节)。
总之,无论是结构体还是类,它们的对象在内存中的布局都遵循相同的对齐规则。在C++中,`struct`和`class`关键字在内存布局方面几乎没有区别,主要区别在于成员的默认访问权限(`struct`的成员默认为`public`,而`class`的成员默认为`private`)。
#include using namespace std; // 计算⼀下A/B/C实例化的对象是多⼤? class A { public: void Print() { cout << _ch << endl; } private: char _ch; int _i; }; class B { public: void Print() { //... } }; class C {}; int main() { A a; B b; C c; cout << sizeof(a) << endl; cout << sizeof(b) << endl; cout << sizeof(c) << endl; return 0; }
上述程序中没有成员变量的B和C类对象的大小是1,为什么没有成员变量还要给1个 字节呢? 因为如果一个字节都不给,怎么表示对象存在过呢!所以这里给1字节,纯粹是为了占位标识对象存在。class Date { public: //void Init(Date*const this,int year, int month, int day) void Init(int year, int month, int day) { this->_year = year; this->_month = month; this->_day = day; } //void Print(Date*const this) void Print() { cout < _year << "/" << this->_month << "/" < _day << endl; } private: // 这⾥只是声明,没有开空间 int _year; int _month; int _day; }; 实际上建议不加this,这里只是了解一下。

#include2.下面程序编译运行结果是(B) A、编译报错 B、运行崩溃 C、正常运行using namespace std; class A { public: void Print(){ cout << "A::Print()" << endl; } private: int _a; }; int main() { A* p = nullptr; p->Print(); return 0; }
#includeusing namespace std; class A { public: void Print() { cout << "A::Print()" << endl; cout << _a << endl; } private: int _a; }; int main() { A* p = nullptr; p->Print(); return 0; }
虽然这个程序可能会在一些编译器上成功编译(取决于编译器的设置和版本),但是当尝试运行时,它会崩溃。这是因为你试图通过一个空指针p调用成员函数Print,并且在Print函数内部,你尝试访问私有成员变量_a。
在C++中,尝试解引用空指针是未定义行为,这意味着程序可能会以任何方式失败。通常情况下,这会导致程序崩溃,因为操作系统会检测到无效的内存访问并终止程序。
3. this指针存在内存哪个区的 (A) A. 栈 B.堆 C.静态区 D.常量区 E.对象里面 在C++中,每当成员函数被调用时,this指针作为隐含的参数传递给函数。这个指针指向调用该函数的对象,其值是对象在内存中的地址。由于this指针是在函数调用时创建的,并且随着函数调用的结束而销毁,因此它通常存储在栈上。本节内容就到此结束啦,谢谢各位友友的阅读,支持小编的点个赞吧,欢迎大家在评论区留言讨论!!!
