大家好,我是小卡皮巴拉
文章目录
目录
一.什么是类
1.1 类的定义
1.2 访问限定符
1.3类域
1.3.1 通过对象访问成员
1.3.2 通过指针访问成员
1.3.3 使用作用域解析运算符访问静态成员
二.实例化
2.1 实例化概念
2.2 对象大小
2.2.1 成员变量
2.2.2 内存对齐
三.this指针
3.1 this指针的定义
3.2 this指针的应用
3.2.1 区分同名的成员变量和局部变量
3.2.2 返回对象自身的引用
3.2.3 获取对象的地址
兄弟们共勉 !!!
每篇前言
博客主页:小卡皮巴拉
咱的口号:🌹小比特,大梦想🌹
作者请求:由于博主水平有限,难免会有错误和不准之处,我也非常渴望知道这些错误,恳请大佬们批评斧正。
一.什么是类
1.1 类的定义
在 C++ 中,类是一种用户自定义的数据类型。它将数据(成员变量)和操作这些数据的函数(成员函数)组合在一起。类的定义以class
关键字开头,后面跟着类名,类名通常采用大写字母开头的单词组合,以符合命名规范。例如:
class MyClass {
private:int myVariable;
public:void setVariable(int value);int getVariable();
};
在这个例子中,MyClass
是一个类,它有一个私有成员变量myVariable
和两个公有成员函数setVariable
和getVariable
。private
和public
是访问修饰符,用于控制类成员的访问权限。
1.2 访问限定符
在 C++ 中,访问限定符用于控制类的成员(包括数据成员和成员函数)的访问权限。C++ 中有三种访问限定符:public
(公有)、private
(私有)和protected
(保护)。
private(私有):
私有成员只能在类的内部被访问。在上面的例子中,myVariable
是私有成员,这意味着它不能被类外部的代码直接访问。这样可以隐藏类的内部实现细节,防止外部代码意外地修改数据,从而增强了数据的安全性和封装性。
class MyClass {
public:// 公有成员函数,用于间接访问私有成员void setPrivateData(int value) {privateData = value;}int getPrivateData() {return privateData;}
private:// 私有数据成员int privateData;
};int main() {MyClass obj;// 通过公有成员函数访问私有数据成员obj.setPrivateData(20);int data = obj.getPrivateData();return 0;
}
public(公有):
公有成员可以在类的外部被访问。setVariable
和getVariable
是公有成员函数,外部代码可以通过创建类的对象来调用这些函数,从而间接地访问私有成员变量。例如:
class MyClass {
public:// 公有成员函数void publicFunction() {// 函数体}// 公有数据成员int publicData;
};int main() {MyClass obj;// 在类外部访问公有成员函数和数据成员obj.publicFunction();obj.publicData = 10;return 0;
}
protected
(保护):
特点:保护成员与私有成员类似,在类的内部可以被自由访问。不同的是,保护成员可以在派生类中被访问,但在类的外部不能被直接访问。
作用:主要用于在继承体系中,让基类的某些成员能够在派生类中被访问和使用,但又不希望在类的外部被随意访问,为继承关系中的数据和函数访问提供了一种中间层次的保护。
class BaseClass {
protected:// 保护数据成员int protectedData;
public:// 公有成员函数void setProtectedData(int value) {protectedData = value;}
};class DerivedClass : public BaseClass {
public:// 派生类中的公有成员函数,访问基类的保护成员void printProtectedData() {// 在派生类中可以访问基类的保护成员std::cout << "Protected data: " << protectedData << std::endl;}
};int main() {DerivedClass obj;obj.setProtectedData(30);obj.printProtectedData();return 0;
}
1.3类域
类定义了一个新的作用域,类的所有成员都在类的作用域中,在类体外定义成员时,需要使用:: 作
用域操作符指明成员属于哪个类域。
1.3.1 通过对象访问成员
类的非静态成员可以通过类的对象来访问,使用点运算符 .
。
#include <iostream>class MyClass {
public:int value;void printValue() {std::cout << "Value: " << value << std::endl;}
};int main() {MyClass obj;obj.value = 10;obj.printValue();return 0;
}
在上述代码中,obj
是 MyClass
类的一个对象,通过 obj.value
访问成员变量 value
,通过 obj.printValue()
调用成员函数 printValue()
。
1.3.2 通过指针访问成员
使用指向类对象的指针时,需要使用箭头运算符 ->
来访问类的成员。
#include <iostream>class MyClass {
public:int value;void printValue() {std::cout << "Value: " << value << std::endl;}
};int main() {MyClass obj;MyClass* ptr = &obj;ptr->value = 20;ptr->printValue();return 0;
}
这里 ptr
是指向 MyClass
对象的指针,通过 ptr->value
访问成员变量,通过 ptr->printValue()
调用成员函数。
1.3.3 使用作用域解析运算符访问静态成员
静态成员属于类本身,而不是类的某个对象,可以使用类名加作用域解析运算符 ::
来访问。
#include <iostream>class MyClass {
public:static int staticValue;static void printStaticValue() {std::cout << "Static Value: " << staticValue << std::endl;}
};// 静态成员变量需要在类外进行初始化
int MyClass::staticValue = 30;int main() {MyClass::printStaticValue();MyClass::staticValue = 40;MyClass::printStaticValue();return 0;
}
在上述代码中,staticValue
是静态成员变量,printStaticValue()
是静态成员函数,通过 MyClass::staticValue
和 MyClass::printStaticValue()
来访问它们。
当在类的成员函数中使用一个名称时,编译器会按照以下顺序查找该名称:
- 局部作用域:首先在成员函数内部查找该名称。
- 类作用域:如果在局部作用域中没有找到,就在类的作用域中查找。
- 全局作用域:如果在类作用域中也没有找到,就在全局作用域中查找。
二.实例化
2.1 实例化概念
用类类型在物理内存中创建对象的过程,称为类实例化出对象。
类是对象进行一种抽象描述,是一个模型一样的东西,限定了类有哪些成员变量,这些成员变量只
是声明,没有分配空间,用类实例化出对象时,才会分配空间。
一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量。
类就好比是设计图纸,而实例化后创建的对象就好比是按照设计图纸建造出来的建筑。
#include <iostream>
#include <string>// 定义 Person 类
class Person {
public:// 成员变量std::string name;// 成员函数void introduce() {std::cout << "Hello, my name is " << name << "." << std::endl;}
};int main() {// 实例化 Person 类的对象Person person1;// 为对象的成员变量赋值person1.name = "Alice";// 调用对象的成员函数person1.introduce();return 0;
}
2.2 对象大小
2.2.1 成员变量
对象大小最主要的影响因素是其成员变量。成员变量的类型和数量决定了对象至少需要多少内存来存储这些数据。
#include <iostream>class SimpleClass {int num; // 4 字节(通常情况下 int 占 4 字节)char ch; // 1 字节
};int main() {SimpleClass obj;std::cout << "Size of SimpleClass object: " << sizeof(obj) << " bytes" << std::endl;return 0;
}
在这个例子中,SimpleClass
包含一个 int
类型和一个 char
类型的成员变量,理论上对象大小至少是 4 + 1 = 5
字节,但实际输出可能不是 5 字节,这涉及到内存对齐。
2.2.2 内存对齐
-
第一个成员在与结构体偏移量为0的地址处。
-
其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
-
注意:对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。
-
VS中默认的对齐数为8
-
结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
-
如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处
-
结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
基本成员变量的内存对齐
#include <iostream>class Example1 {char c; // 1 字节int i; // 4 字节short s; // 2 字节
};int main() {std::cout << "Size of Example1: " << sizeof(Example1) << " bytes" << std::endl;return 0;
}
-
char c
占 1 字节。 -
由于
int
类型要求 4 字节对齐,char c
之后需要填充 3 字节,使得int i
能从 4 的整数倍地址开始存储。 -
short s
占 2 字节,它本身要求 2 字节对齐,在int i
之后可以直接存储。 -
为了保证整个对象的大小是最大成员(
int
,4 字节)的整数倍,最后可能还需要填充 2 字节。所以Example1
对象的总大小通常是 12 字节。
调整成员变量顺序以减少填充
#include <iostream>class Example2 {char c; // 1 字节short s; // 2 字节int i; // 4 字节
};int main() {std::cout << "Size of Example2: " << sizeof(Example2) << " bytes" << std::endl;return 0;
}
-
char c
占 1 字节。 -
short s
占 2 字节,它要求 2 字节对齐,在char c
之后无需额外填充,可直接存储。此时已占用 3 字节。 -
为了让
int i
从 4 的整数倍地址开始,在short s
之后填充 1 字节。 -
所以
Example2
对象的总大小是 8 字节,通过调整成员变量顺序减少了填充字节数。
包含结构体成员的内存对齐
#include <iostream>struct InnerStruct {char c; // 1 字节int i; // 4 字节
};class Example3 {InnerStruct is; // 结构体成员short s; // 2 字节
};int main() {std::cout << "Size of Example3: " << sizeof(Example3) << " bytes" << std::endl;return 0;
}
-
InnerStruct
中,char c
占 1 字节,为了让int i
4 字节对齐,需填充 3 字节,所以InnerStruct
的大小是 8 字节。 -
short s
占 2 字节,它要求 2 字节对齐,在InnerStruct
之后可以直接存储。 -
为了保证整个对象的大小是最大成员(
InnerStruct
内的int
,4 字节)的整数倍,这里无需额外填充。所以Example3
对象的总大小是 10 字节。
三.this指针
Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用Init和
Print函数时,该函数是如何知道应该访问的是d1对象还是d2对象呢?那么这里就要看到C++给了
一个隐含的this指针解决这里的问题。
3.1 this指针的定义
this
指针是一个隐含于每一个非静态成员函数中的特殊指针。它指向调用该成员函数的对象,也就是说,当你在一个对象上调用成员函数时,编译器会自动将该对象的地址作为this
指针传递给成员函数。
下面来具体解释什么是this指针
编译器编译后,类的成员函数默认都会在形参第一个位置,增加一个当前类类型的指针,叫做this
指针。
比如Date类的Init的真实原型为,void Init(Date* const this, int year, int month, int day)
类的成员函数中访问成员变量,本质都是通过this指针访问的,如Init函数中给_year赋值, this-
>_year = year;
注意:C++规定不能在实参和形参的位置显式的写this指针(编译时编译器会处理),但是可以在函数体内显式使用this指针。
3.2 this指针的应用
3.2.1 区分同名的成员变量和局部变量
#include <iostream>class Person { private:std::string name;int age; public:// 构造函数,使用this指针区分同名的参数和成员变量Person(const std::string& name, int age) {this->name = name;this->age = age;}void display() {std::cout << "Name: " << name << ", Age: " << age << std::endl;} };int main() {Person p("Alice", 20);p.display();return 0; }
在上述代码中,构造函数
Person
的参数名name
和age
与成员变量同名。使用this->name
和this->age
可以明确地引用成员变量,避免混淆。
3.2.2 返回对象自身的引用
#include <iostream>class Number { private:int value; public:Number(int value) : value(value) {}// 成员函数,返回对象自身的引用Number& add(int num) {this->value += num;return *this;}void display() {std::cout << "Value: " << value << std::endl;} };int main() {Number num(10);num.add(5).add(3); // 链式调用num.display();return 0; }
在上述代码中,
add
成员函数返回*this
,即返回当前对象的引用。这样就可以实现链式调用,连续调用多个add
函数。
3.2.3 获取对象的地址
#include <iostream>class MyClass { public:void printAddress() {std::cout << "The address of this object is: " << this << std::endl;} };int main() {MyClass obj;obj.printAddress();return 0; }
在上述代码中,
printAddress
成员函数使用this
指针输出当前对象的地址。
兄弟们共勉 !!!
码字不易,求个三连
抱拳了兄弟们!