C++————类和对象(一)
1.类定义格式
在C++中,类(class)是封装数据和操作这些数据的函数的构造。类的定义包含成员变量和成员函数。
类的基本定义格式如下:
class ClassName {
// 访问修饰符
public:
// 公有成员
DataType memberVariable; // 成员变量
void memberFunction() { // 成员函数
// 函数体
}
private:
// 私有成员(可选)
DataType privateVariable;
protected:
// 受保护成员(可选)
DataType protectedVariable;
- 成员变量:类中用于存储数据的变量,数据类型由
DataType
表示。 - 成员函数:类中的函数,可以操作类的成员变量。
示例:
#include <iostream>
using namespace std;
class Car {
public:
string brand; // 公有成员变量
int year;
// 公有成员函数
void start() {
cout << "The car is starting." << endl;
}
};
int main() {
// 创建对象
Car myCar;
// 访问对象的成员
myCar.brand = "Toyota";
myCar.year = 2021;
cout << "Brand: " << myCar.brand << ", Year: " << myCar.year << endl;
myCar.start(); // 调用成员函数
return 0;
}
- 为了区分成员变量,⼀般习惯上成员变量会加⼀个特殊标识,如成员变量前面或者后面加_或者m 开头。
示例代码:
使用 _ 前缀:
cpp
#include <iostream>
using namespace std;
class Car {
public:
string _brand; // 使用 _ 前缀作为成员变量的标识
int _year;
void start() {
cout << "The car is starting." << endl;
}
};
int main() {
Car myCar;
myCar._brand = "Toyota";
myCar._year = 2021;
cout << "Brand: " << myCar._brand << ", Year: " << myCar._year << endl;
myCar.start();
return 0;
}
这些做法有助于提高代码的可读性,尤其是在类中存在多个变量时,能够快速识别出哪些是成员变量,可以帮助提高代码的清晰度,尤其是在较大的项目中。
- C++中struct也可以定义类,C++兼容C中struct的用法,同时struct升级成了类,明显的变化是 struct中可以定义函数,⼀般情况下我们还是推荐用class定义类。
#include<iostream>
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++ 中,类内定义的成员函数默认是 inline
的。这意味着,如果成员函数的实现出现在类的定义内部,编译器通常会尝试将该函数进行内联替换。
这也有一些好处:
- 减少函数调用开销:通过将函数的代码直接插入到调用的地方,避免了传统的函数调用机制(如栈操作、跳转等)的开销。
- 适用于短小函数:
inline
通常适用于小型函数,尤其是成员函数。如果函数体非常简单,编译器会倾向于将其内联。
1.1 访问限定符
在 C++ 中,访问限定符(Access Specifiers) 用于控制类的成员(包括数据成员和成员函数)在类外部的访问权限。C++ 中有三种主要的访问限定符:public
、private
和 protected
。这些限定符决定了类的成员在类外如何被访问。
1.1.1 public
- 描述:
public
成员是最宽松的,它允许类的外部代码访问这些成员。 - 适用场景:通常用于希望外部能够直接访问或修改的数据成员和成员函数,例如提供接口方法、获取和设置数据等。
示例:
class MyClass {
public:
int publicValue; // 公有成员
void display() { // 公有成员函数
std::cout << "Public value: " << publicValue << std::endl;
}
};
int main() {
MyClass obj;
obj.publicValue = 10; // 可以直接访问公有成员
obj.display(); // 可以直接调用公有成员函数
return 0;
}
1.1.2 private
- 描述:
private
成员只能在类的内部访问,类的外部无法直接访问这些成员。即便是派生类也不能访问基类的private
成员。 - 适用场景:通常用于将类的内部实现细节隐藏起来,防止外部代码直接修改,确保数据封装和类的安全性。
示例:
class MyClass {
private:
int privateValue; // 私有成员
public:
void setValue(int value) { // 公有成员函数,用于修改私有成员
privateValue = value;
}
void display() {
std::cout << "Private value: " << privateValue << std::endl;
}
};
int main() {
MyClass obj;
// obj.privateValue = 10; // 错误,不能直接访问私有成员
obj.setValue(10); // 可以通过公有成员函数间接访问
obj.display();
return 0;
}
1.1.3 protected
- 描述:
protected
成员既不能被类外部访问,也不能被外部对象直接访问。但是,派生类可以访问基类的protected
成员(但不能被外部直接访问)。因此,protected
成员适用于希望在类的外部隐藏,但允许派生类访问的成员。 - 适用场景:通常用于需要继承的类中,提供一个在派生类中可访问的成员。
protected
与 private
的区别
private
成员只能在类的成员函数内部访问,派生类和外部代码都无法直接访问。protected
成员只能在类的成员函数和派生类中访问,外部代码不能访问。
示例:
#include <iostream>
using namespace std;
class Base {
protected:
int protectedValue; // 保护成员
public:
Base() : protectedValue(0) {}
void setProtectedValue(int value) {
protectedValue = value;
}
void display() {
cout << "Protected value: " << protectedValue << endl;
}
};
class Derived : public Base {
public:
void modify() {
protectedValue = 100; // 派生类可以访问基类的保护成员
}
};
int main() {
Base baseObj;
baseObj.setProtectedValue(10);
baseObj.display(); // 输出: Protected value: 10
Derived derivedObj;
derivedObj.modify();
derivedObj.display(); // 输出: Protected value: 100
// baseObj.protectedValue = 200; // 错误,不能访问保护成员
// std::cout << baseObj.protectedValue << endl; // 错误,不能直接访问保护成员
return 0;
}
1.1.4 成员的默认访问级别
- 如果在类中没有指定访问级别:
- 在
class
中,默认是private
。 - 在
struct
中,默认是public
。
- 在
示例:
class MyClass {
int value; // 默认是 private
};
struct MyStruct {
int value; // 默认是 public
};
//有遗漏的地方,再后续的类和对象的学习中会有补充!
1.2.类域
- 类定义了⼀个新的作用域,类的所有成员都在类的作用域中,在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
- 类域影响的是编译的查找规则,下⾯程序中Init如果不指定类域Stack,那么编译器就把Init当成全 局函数,那么编译时,找不到array等成员的声明/定义在哪里,就会报错。指定类域Stack,就是知 道Init是成员函数,当前域找不到的array等成员,就会到类域中去查找。
#include<iostream>
using namespace std;
class Stack
{
public:
//成员函数
void Init(int n = 4);
private:
// 成员变量
int* array;
size_t capacity;
size_t top;
};
// 声明和定义分离,需要指定类域
void Stack::Init(int n)
{
array = (int*)malloc(sizeof(int) * n);
if (nullptr == array)
{
perror("malloc申请空间失败");
return;
}
capacity = n;
top = 0;
}
int main()
{
Stack st;
st.Init();
return 0;
}
2. 实例化
在 C++ 中,实例化(Instantiation)指的是根据一个类(class)的定义创建一个具体的对象。通过实例化,我们可以生成类的具体对象并在内存中分配空间。这个过程使得类变成可以操作和使用的实体。
2.1实例化的过程
实例化实际上是将类作为模板,创建出类的对象。对象是类的实例,它拥有类定义的属性和行为。实例化过程分为两个主要步骤:
- 定义类:类是一个模板,包含成员变量(属性)和成员函数(方法),它只是一个概念上的蓝图。
- 创建对象:通过类定义创建一个对象,分配内存并初始化对象的成员变量。
2.2 实例化的举例
假设我们有一个简单的 Car
类,定义了车的属性(如颜色和速度)以及行为(如启动和停车)。
2.2.1 类的定义
class Car {
public:
// 成员变量
string color;
int speed;
// 构造函数
Car(string c, int s) : color(c), speed(s) {}
// 成员函数
void start() {
cout << "The car is starting." << endl;
}
void stop() {
cout << "The car is stopping." << endl;
}
};
在这个类定义中,Car
类具有成员变量 color
和 speed
,以及成员函数 start()
和 stop()
。
2.2.2 实例化对象
实例化对象就是根据类模板创建出一个具体的对象。在 main
函数中,我们实例化了一个 Car
对象。
int main() {
Car myCar("Red", 100); // 实例化 Car 类对象 myCar
myCar.start(); // 调用 myCar 对象的 start 函数
cout << "The color of the car is: " << myCar.color << endl; // 输出 car 的颜色
return 0;
}
在这个代码片段中,Car myCar("Red", 100);
就是实例化过程,我们创建了一个名为 myCar
的对象,它是 Car
类的一个实例。myCar
被初始化为红色,并且速度为 100。
myCar.start();
和 myCar.color
展示了如何使用实例化出来的对象。
2.3 对象大小
在C++中,实例化对象时涉及到对象的大小,这个大小是由对象中包含的成员变量(数据成员)决定的。我们可以通过对对象大小的了解,进一步优化程序的内存管理。
- 第⼀个成员在与结构体偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
- 注意:对齐数=编译器默认的⼀个对齐数与该成员大小的较⼩值。
- VS中默认的对齐数为8
- 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小 就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
结构体对齐示例
#include <iostream>
using namespace std;
struct Example {
char c; // 1 字节
int i; // 4 字节
};
int main() {
cout << "Size of Example: " << sizeof(Example) << " bytes" << endl;
return 0;
}
输出可能为:
Size of Example: 8 bytes
//后续会有补充