C++中传递参数是指针类型以及传入参数是指针的指针(**)详解

C++中传递参数是指针时,在函数内部,其实是会复制一份新的指针,只不过这两个指针指向的是同一块内存地址

首先我们要明白一点,在C++传递参数时,不论是传入指针还是传入值,传入函数后都会在函数内部创建一个副本 => 也就是说,传入前的指针或是值不会变. 但是指针有个点就是,函数内部的这个指针副本,和外面的这个指针,它们指向的是同一块内存地址。这样,我们可以通过在函数中传入指针参数,来修改指针指向的值,但是不能修改指针本身(因为函数内部是复制了一份指针副本)

 

那么,我们现在来看一个问题,比如现在有一个int类型的变量a, 我现在需要在一个函数modifyA()内部修改这个变量a的值? 有什么方法

main()
{
      int a = 5;      
}

ModifyA()
{
    
}

显然,如果我把a作为参数传入到modifyA()方法中去,显然会在方法modifyA内部复制一个整形变量作为a的副本,达不到修改变量a的值的目的。 在C++中,有两种方法可以达到修改a的值的目的

1. 传入a的引用,把a的引用作为ModifyA()函数的参数 =>  ModifyA(int& x);

2. 传入一个整形的指针int* p, 这个指针p指向a => 这样,在函数ModifyA内部会复制一份指针,但是这个复制的指针同样也会指向a, 它和原来的指针p指向同一个内存地址。这样,我们可以修改a的值

上面我们是想修改int类型的变量a的值,需要这样操作。那么同理,如果我们修改的不是int类型的变量,也就是说,变量类型不是int类型,而是指针类型,比如我们需要在函数内部修改指针类型char*类型的变量,应该怎么办? 同理:

1. 传入该类型的引用 char*& (int类型的引用是int&, char*类型的引用自然就是char*&)

2. 传入指向该类型的指针,指向char*类型的指针是 char** => 也就是本文要讨论的  指向指针的指针

我们来看一个非常经典的用malloc函数分配内存的例子

void AllocateMemory(char* p) //函数的参数是指针类型,那么在调用这个函数时,应该传入的是参数的地址
{
      p = malloc(sizeof(int)*10);
      return;
}

void TestMethod()
{
char* pointer = null;
AllocateMemory(pointer); //这里,传入的是pointer的一个副本
}


 

在函数AllocateMemory内部,将会产生一个pointer的副本,这个副本和TestMethod中的pointer指向同一块内存地址A1(为了方便讲解,我们给这个地址取名A1)

 

然后在函数AllocateMemory内部,通过调用malloc函数,给pointer的副本指向了一个新分配的地址,我们把它叫做内存地址A2, 所以现在变成这样

 

所以,我们可以看到,这段代码,TestMethod()中的pointer指向没有任何变化,调用完AllocateMemory函数后,它还是指向原来的内存地址,它并不会指向malloc函数新分配的内存地址A2

但是,我们的本意是想用malloc函数重新分配一块内存地址,然后TestMethod方法中的指针pointer能够指向这块新分配的内存,要达到这个目标,需要怎么做呢? 我们这样改写代码

void AllocateMemory2(char* *p) //函数的参数是指向char*类型的指针的指针
{
      *p = malloc(sizeof(int)*10);
      return;
}

void TestMethod2()
{
   char* pointer = null;
   AllocateMemory(&pointer);  //这里,传入的是pointer的一个副本
}

我们来理解char* *p

 p是一个指针,它指向的值是*p, 它指向的这个值本身又是一个char*类型的指针 => 这个指针又指向另一块内存地址(存放一个char类型的值)

所以明白了吧,在方法TestMethod2中, pointer是一个char*类型的指针,也就是上面图中中间方框的那个,而最左边方框中其实是中间方框中的地址,所以也就是&pointer,这也就是我们需要传入AllocateMemory中的参数, 所以调用时,传入的是

AllocateMemory(&pointer); => 调用时,同理,会复制一份&pointer的副本

 

在方法里面,我们把用malloc方法新分配的内存赋给了pointer指向的内容,也就是说

 

 

 

显然,目的达到

了解了上面这些,现在我们来看看在开发中遇到的一个问题,当时大概是这样的ClientDlg.hClientDlg.h

class ClientDlg : public CDialog  //CDialog是MFC的afxwin里面的
{
ClientDlg* m_next_ptr;

}

PasswordDlg.h
Class PasswordDlg : ClientDlg
{
}

TestDlg.cpp
TestDlg : ClientDlg
{
void TestDlg::Init()
{
        m_next_ptr = NULL;
    } 

void TestDlg::Method1
{
.......... //some code here

m_next_ptr = new PasswordDlg();
m_next_ptr->create();
m_next_ptr->showWindow(SW_NORMAL);
}
}

现在有一个新的Helper.cpp, 要把上面TestDlg的Method1中的那三行提成一个新的方法,放在Helper.cpp里面,刚开始我是这么写的

Helper.cpp
void Helper::ShowPasswordWindow(ClientDlg* dlgNext)
{
dlgNext = new PasswordDlg();
dlgNext->create();
dlgNext->showWindow(SW_NORMAL):

}

TestDlg.cpp
TestDlg : ClientDlg
{
#include "Helper.h"

   void TestDlg::Init()
{
        m_next_ptr = NULL;
   } 
   
void TestDlg::Method1
{
.......... //some code here

Helper::ShowPasswordWindow(m_next_ptr):
}
}

 这个代码是有问题,就是因为函数 ShowPasswordWindow(ClientDlg* dlgNext) 中传入ClientDlg*的指针时,会复制一份指针dlgNext的副本 _dlgNext, 也就是说,我们的目标本来是把TestDlg中的 m_next_ptr, 让它是一个PasswordDlg窗体

m_next_ptr = new PasswordDlg(); 

但实际上呢,我们调用ShowPasswordWindow(m_next_ptr)后,里面会创建一个m_next_ptr的副本 copy_m_next_ptr, 所以其实是

copy_m_next_ptr = new PasswordDlg(); 而TestDlg.cpp中的 m_next_ptr依然还是NULL  => 这显然和初衷不符, 那么,如果我们要达到初衷 =》更改TestDlg.cpp中的 m_next_ptr, 让它成为一个PasswordDlg窗体,我们应该怎么做呢

这个时候我们就要用到上面提到的指向指针的指针,m_next_ptr是一个ClientDlg*的指针,我们需要另一个指针 new_pointer来指向它,也就是说new_pointer的内存中存放的是指针m_next_ptr的地址 &m_next_ptr

这样,在Helper::ShowPasswordWindow()方法中传入的参数是这个指向m_next_ptr的指针new_pointer, 方法里面会复制一份new_pointer的副本copy_new_pointer,但是这个副本copy_new_pointer指向的也是m_next_ptr,这样,就可以保证修改到m_next_ptr

显然, 这里的 new_pointer是一个指针,它指向的是另一个指针m_next_ptr, 所以它是指向指针的指针,这也就是 Helper::ShowPasswordWindow()的参数, 修改后的代码如下

Helper.cpp
void Helper::ShowPasswordWindow(ClientDlg** dlgNext)
{
*dlgNext = new PasswordDlg();
*dlgNext->create();
*dlgNext->showWindow(SW_NORMAL):

}

TestDlg.cpp
TestDlg : ClientDlg
{
#include "Helper.h"

   void TestDlg::Init()
{
        m_next_ptr = NULL;
   } 
   
void TestDlg::Method1
{
.......... //some code here

Helper::ShowPasswordWindow(&m_next_ptr):
}
}

 

 

 

 

 

 

 

 

 

 

 




posted on 2024-08-07 17:08  新西兰程序员  阅读(295)  评论(0编辑  收藏  举报