C++学习(二)(指针和引用)
delete ptr;return 0;注意:请记住,由于 unique_ptr 具有独占所有权,因此当您需要对对象的共享访问权限时,不能使用它。对于此类情况,您可以使用 .原始指针新建/删除运算符(Raw 指针 和 and 运算符deleteC++ 中的原始指针是直接保存内存地址的低级构造。它们可用于手动分配内存、创建动态数组和高效传递值等。new算子:该运算符用于在堆上分配内存。分配的内存将保
指针
概念:
指针是存储另一个变量(或函数)的内存地址的变量。它指向变量在内存中的位置,并允许您间接访问或修改该值。
代码:
#声明指针的一般格式:
dataType *pointerName;
#初始化指针
int num = 10;
int *ptr = # // Pointer 'ptr' now points to the memory address of 'num'
#使用指针访问值
int value = *ptr; // Value now contains the value of the variable that 'ptr' points to (i.e., 10)
#函数指针
int add(int a, int b)
{
return a + b;
}
int main()
{
int (*funcptr) (int, int) = add; // Pointer 'funcptr' now points to the functions 'add'
funcptr(4, 5); // Return 9
}
引用
概念:
引用是现有变量的别名,这意味着它是同一内存位置的不同名称。
注意:
与指针不同,引用不能为 null,并且必须在声明时初始化它们。一旦初始化了引用,就不能将其更改为引用另一个变量。引用可以被视为常量指针(不要与指向常量值的指针混淆),它始终指向(引用)同一对象。它们使用 (& 符号) 进行声明。&
声明和初始化:
要声明引用,请使用 symbol 后跟变量类型和引用的名称。请注意,您必须在声明引用时对其进行初始化。&
int var = 10; // Declare an integer variable
int& ref = var; // Declare a reference that "points to" var
用法 :
您可以像使用原始变量一样使用引用。当您更改引用的值时,原始变量的值也会更改,因为它们共享相同的内存位置。
var = 20; // Sets the value of var to 20
std::cout << ref << std::endl; // Outputs 20
ref = 30; // Sets the value of ref to 30
std::cout << var << std::endl; // Outputs 30
功能参数:
您可以使用引用作为函数参数来创建参数的别名。当您需要修改原始变量或传递相当大的对象以避免复制成本时,通常会这样做。
void swap(int& a,int& b){
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 5, y = 10;
std::cout << "Before Swap: x = " << x << " y = " << y << std::endl; // Outputs 5 10
swap(x, y);
std::cout << "After Swap: x = " << x << " y = " << y << std::endl; // Outputs 10 5
}
#以下是声明引用的一般格式:
dataType &referenceName = existingVariable;
#例:
int num = 10;
int &ref = num; // Reference 'ref' is now an alias of 'num'
修改 的值 也会修改 的值 ,因为它们共享相同的内存位置。ref num
注意:
注意:如果要在函数参数中通过引用传递变量,或者想要为变量创建别名而无需指针语法,则通常使用引用。
C++中的内存模型
概念:
C++ 中的内存模型定义程序如何在计算机内存中存储和访问数据。它由不同的段组成,例如 Stack、Heap、Data 和 Code 段。这些段中的每一个都用于存储不同类型的数据,并具有特定的特征。
堆栈内存:堆栈内存用于自动存储持续时间变量,例如局部变量和函数调用数据。堆栈内存由编译器管理,其分配和释放是自动完成的。堆栈内存也是 LIFO (后进先出) 数据结构,这意味着最近分配的数据是第一个被释放的数据。
void functionExample() {
int x = 10; // x is stored in the stack memory
}
堆内存:堆内存用于动态存储持续时间变量,例如使用关键字创建的对象。程序员可以使用 and 运算符控制堆内存的分配和释放。堆内存是比堆栈更大的内存池,但访问时间较慢。new new delete
void functionExample() {
int* p = new int; // dynamically allocated int in heap memory
*p = 10;
// more code
delete p; // deallocate memory
}
数据区段:数据段由两部分组成:初始化的数据段和未初始化的数据段。已初始化的数据段存储具有初始值的全局、静态和常量变量,而未初始化的数据段存储未初始化的全局和静态变量。
// Initialized data segment
int globalVar = 10; // global variables
static int staticVar = 10; // static local variables
const int constVar = 10; // constant variables with value
// Uninitialized data segment
int globalVar; // uninitialized global variables
代码段:Code 段(也称为 Text 段)存储程序的可执行代码(机器代码)。它通常位于内存的只读区域中,以防止意外修改。
void functionExample() {
// The machine code for this function is stored in the code segment.
}
C++中的对象生存期
Static Storage Duration(静态存储持续时间):具有 static Storage Duration 的对象在程序的整个运行过程中都存在。这些对象在程序运行开始时分配,并在程序终止时解除分配。全局变量、静态数据成员和静态局部变量属于此类别。
int global_var; // Static storage duration
class MyClass {
static int static_var; // Static storage duration
};
void myFunction() {
static int local_var; // Static storage duration
}
线程存储持续时间:具有线程存储持续时间的对象在其所属线程的生命周期内存在。它们是在线程启动时创建的,在线程退出时销毁。可以使用 关键字 指定线程存储持续时间。thread_local
thread_local int my_var; // Thread storage duration
自动存储持续时间:具有自动存储持续时间的对象在定义时创建,并在退出声明它们的作用域时销毁。这些对象也称为 “local” 或 “stack” 对象。函数参数和局部非静态变量属于这一类。
void myFunction() {
int local_var; // Automatic storage duration
}
Dynamic Storage Duration:具有动态存储持续时间的对象是在运行时使用内存分配函数(如 或 )创建的。必须手动管理这些对象的生存期,因为在退出范围时,它们不会自动解除分配。相反,程序员有责任在不再需要对象时使用 or 函数销毁对象,以避免内存泄漏。new malloc delete free
int* ptr = new int; // Dynamic storage duration
delete ptr;
智能指针
弱指针:
A 是 C++ 中的一种智能指针,它为原始指针添加了间接级别和安全性。它主要用于在两个对象具有指向彼此的共享指针的情况下,或者当您需要对由 .weak_ptr shared_ptr
A 不会增加它指向的对象的所有权引用计数,这是 和 之间的关键区别。与对象关联的控制块维护两个计数:一个用于 s 的数量 (所有权计数),另一个用于 s 的数量 (弱计数)。s 的存在不会阻止删除对象;一旦最后一个拥有该对象被销毁或重置,该对象就会被销毁,即使 S 仍在引用该对象。但是,在所有权计数达到零并且弱计数也达到零之前,控制块本身不会被释放,从而允许 s 安全地检测对象是否已被删除。weak_ptr weak_ptr shared_ptr shared_ptr weak_ptr weak_ptr shared_ptr weak_ptrweak_ptr
要使用 ,您必须使用函数将其转换为 a,该函数会尝试创建一个共享对象所有权的新函数。如果成功,则对象的引用计数将增加,您可以使用返回的 来安全地访问该对象。weak_ptr shared_ptr lock() shared_ptr shared_ptr
#下面是一个使用 :weak_ptr
#include <iostream>
#include <memory>
class MyClass {
public:
void DoSomething() {
std::cout << "Doing something...\n";
}
};
int main() {
std::weak_ptr<MyClass> weak;
{
std::shared_ptr<MyClass> shared = std::make_shared<MyClass>();
weak = shared;
if (auto sharedFromWeak = weak.lock()) {
sharedFromWeak->DoSomething(); // Safely use the object
std::cout << "Shared uses count: " << sharedFromWeak.use_count() << '\n'; // 2
}
}
// shared goes out of scope and the MyClass object is destroyed
if (auto sharedFromWeak = weak.lock()) {
// This block will not be executed because the object is destroyed
}
else {
std::cout << "Object has been destroyed\n";
}
return 0;
}
共享指针:
A 是 C++ 中的一种智能指针,它允许多个指针共享动态分配的对象的所有权。仅当指向它的最后一个对象被销毁时,该对象才会自动解除分配。shared_ptr shared_ptr
使用 a 时,每次创建新指针时,引用计数器都会自动递增,当每个指针超出范围时,引用计数器都会递减。一旦 reference counter 达到零,系统将清理内存。shared_ptr
#下面是一个如何使用的示例:shared_ptr
#include <iostream>
#include <memory>
class MyClass {
public:
MyClass() { std::cout << "Constructor is called." << std::endl; }
~MyClass() { std::cout << "Destructor is called." << std::endl; }
};
int main() {
// create a shared pointer to manage the MyClass object
std::shared_ptr<MyClass> ptr1(new MyClass());
{
// create another shared pointer and initialize it with the previously created pointer
std::shared_ptr<MyClass> ptr2 = ptr1;
std::cout << "Inside the inner scope." << std::endl;
// both pointers share the same object, and the reference counter has been increased to 2
}
std::cout << "Outside the inner scope." << std::endl;
// leaving the inner scope will destroy ptr2, and the reference counter is decremented to 1
// the main function returns, ptr1 goes out of scope, and the reference counter becomes 0
// this causes the MyClass object to be deleted and the destructor is called
}
#输出:
Constructor is called.
Inside the inner scope.
Outside the inner scope.
Destructor is called.
#在此示例中,并共享同一对象的所有权。仅当两个指针都超出范围并且引用计数器变为零时,才会销毁该对象。ptr1ptr2
例子:
1. 想象一栋房子(对象)
-
shared_ptr就像 房客,他们直接租了房子,每搬进来一个房客,房客计数器+1。 -
weak_ptr就像 中介,中介只是知道房子的存在,但不会住进去,所以房客计数器不变,但中介计数器+1。
2. 房子什么时候会被拆(对象销毁)?
-
规则1:当所有房客(
shared_ptr) 都搬走(销毁),房子会被立刻拆除(对象销毁)。 -
规则2:但「中介信息牌」(控制块)会一直保留,直到中介(
weak_ptr) 也全部离开(计数器归零),才会被清理。
3. 中介(weak_ptr)有什么用?
-
场景1:防止房客互相「死锁」
比如房客A和房客B互相持有对方的钥匙(循环引用),导致谁都无法搬走,房子永远无法拆除(内存泄漏)。
解决方法:让其中一方改用中介(weak_ptr),不增加房客计数器。 -
场景2:临时查看房子是否还在
中介可以用.lock()方法尝试联系房东:-
如果房子还在(还有房客),就临时拿到钥匙(返回有效的
shared_ptr),房客计数器+1。 -
如果房子已拆(没有房客了),返回空钥匙(
nullptr)。
-
#include <memory>
#include <iostream>
class House {
public:
~House() { std::cout << "房子拆了!\n"; }
};
int main() {
// 房客1(shared_ptr)搬进来
std::shared_ptr<House> tenant1 = std::make_shared<House>();
// 中介(weak_ptr)记录房子信息
std::weak_ptr<House> agent = tenant1;
// 房客1搬走,此时房客计数器归零,房子被拆
tenant1.reset();
// 中介尝试查看房子
if (auto temp_tenant = agent.lock()) {
std::cout << "房子还在!\n";
} else {
std::cout << "房子已经拆了!\n"; // 会执行这里
}
return 0;
}
总结:
唯一指针 (unique_ptr)
概念:
std::unique_ptr是由 C++ 标准库提供的智能指针。它是一个模板类,用于管理单个对象或数组。
unique_ptr适用于独占所有权的概念 - 这意味着一次只允许一个人拥有一个对象。此所有权可以转让或移动,但不能共享或复制。unique_ptr
此概念有助于防止指针悬空等问题,减少内存泄漏,并消除手动内存管理的需求。当 超出范围时,它会自动删除它拥有的对象。unique_ptr
创建 unique_ptr
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int> p1(new int(5)); // Initialize with pointer to a new integer
std::unique_ptr<int> p2 = std::make_unique<int>(10); // Preferred method (C++14 onwards)
std::cout << *p1 << ", " << *p2 << std::endl;
return 0;
}
转让所有权
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int> p1(new int(5));
std::unique_ptr<int> p2 = std::move(p1); // Ownership is transferred from p1 to p2
if (p1) {
std::cout << "p1 owns the object" << std::endl;
} else if (p2) {
std::cout << "p2 owns the object" << std::endl;
}
return 0;
}
将 unique_ptr 与自定义删除程序结合使用
#include <iostream>
#include <memory>
struct MyDeleter {
void operator()(int* ptr) {
std::cout << "Custom Deleter: Deleting pointer" << std::endl;
delete ptr;
}
};
int main() {
std::unique_ptr<int, MyDeleter> p1(new int(5), MyDeleter());
return 0; // Custom Deleter will be called when p1 goes out of scope
}
注意:请记住,由于 unique_ptr 具有独占所有权,因此当您需要对对象的共享访问权限时,不能使用它。对于此类情况,您可以使用 .std::shared_ptr
原始指针
新建/删除运算符(Raw 指针 和 and 运算符new delete)
概念:C++ 中的原始指针是直接保存内存地址的低级构造。它们可用于手动分配内存、创建动态数组和高效传递值等。new算子:
该运算符用于在堆上分配内存。分配的内存将保持可用,直到您使用相应的运算符显式取消分配它。new newdelete
下面是使用 Operator 的示例:new
int* ptr = new int; // Dynamically allocates an int on the heap
*ptr = 42; // Assigns the value 42 to the allocated int
delete算子
该运算符用于释放已使用 分配的内存。释放内存后,可以将其重新分配用于其他目的。未能正确释放内存可能会导致内存泄漏。deletenew
下面是使用 Operator 的示例:delete
int* ptr = new int; // Dynamically allocates an int on the heap
*ptr = 42; // Assigns the value 42 to the allocated int
delete ptr; // Deallocates the memory assigned to ptr
and 运算符用于为对象数组分配和取消分配内存。和 的语法与 和 的语法非常相似。new[] delete[] new[] delete[] new delete
下面是使用 and 运算符的示例:new[] delete[]
int n = 10;
int* arr = new int[n]; // Dynamically allocates an array of 10 integers on the heap
// Set some values in the array
for (int i = 0; i < n; i++) {
arr[i] = i;
}
delete[] arr; // Deallocates the memory assigned to the array
总之,原始指针和 and 运算符允许在 C++ 中手动管理内存,从而提供对分配和释放的控制。确保始终取消分配使用 或 分配的内存,以避免程序中的内存泄漏。new delete new new[]
内存泄漏
概念:
当程序在堆中分配内存,但在不再需要内存时未将内存释放回作系统时,就会发生内存泄漏。随着时间的推移,这会导致可用内存耗尽,从而导致系统性能低下或崩溃。
在 C++ 中,当您使用原始指针时,您需要手动管理内存分配和释放。在许多情况下,您将使用 keyword 为堆中的对象分配内存,并在不再需要该内存时使用 keyword 来释放该内存。忘记执行此作可能会导致内存泄漏。new delete
void create_memory_leak() {
int* ptr = new int[100]; // Allocating memory in the heap for an array of integers
// Some code...
// Code to deallocate the memory is missing: delete[] ptr;
} // ptr goes out of scope, memory block allocated is not deallocated, causing a memory leak.
更多推荐


所有评论(0)