【问题标题】:What happens when I "re-initialize" a pointer当我“重新初始化”指针时会发生什么
【发布时间】:2016-08-28 21:15:15
【问题描述】:

我目前正在用 C++ 编写一段代码,但遇到了一些我不理解的行为。假设我有一个自定义类(我不确定类内部发生的事情是否重要)并且我想创建一个指向该类的指针:

AliAODEvent* aod;

现在,在我创建了这个指针之后,我想将它分配给一个内存位置。在我正在使用的框架中,我要指向的对象已经存在于某个地方,所以我所做的是

AliAODEvent* aod = (This is where the object I want to point to goes);

我现在意识到我应该只使用aod=(Object to point to); 但我很好奇当我试图基本上“重新初始化”指针时发生了什么。有什么想法吗?

【问题讨论】:

  • 你应该得到某种编译错误,比如error: redefinition of 'aod'
  • 除了已经说过的内容之外:请务必注意,与指针不同,引用不能被重新分配。除此之外,重新分配指针并没有什么特别之处,因为它只是一个保存内存地址的整数变量(假设是一种简化的方法)。
  • 我想将它分配给一个内存位置” - 请确保您理解术语“assign to”。在这里,您正在为指针分配一个值,反之亦然。这是基本的,注意你在编程中如何表达。

标签: c++ pointers initialization


【解决方案1】:

在 C++ 中,你有 declarationinitialisation

int x;    // Declaration
x = 10;   // Initialisation

声明一个变量意味着要求操作系统在内存中为它分配一个位置。初始化变量意味着第一次在该内存地址设置值。你可以同时做这两个:

int x = 10;   // Declares x, then initialises it to 10

在 C++ 中,不允许多次声明一个变量:

int x;   // Declares x
int x;   // Tries to declare another variable also called x, fails (ERROR)

但是,您可以声明两个具有相同名称的变量,只要它们位于不同的范围内:

#include <iostream>
int x = 10;

int main()
{

    int x = 20;

    {
        int x = 30;
        std::cout << x << ", " << ::x << std::endl;   // Prints '30, 10'
    }

    std::cout << x << ", " << ::x << std::endl;   // Prints '20, 10'

    return 0;
}

{} 表示新作用域, :: 表示全局作用域。所以你可以指定全局作用域,也可以使用局部作用域,但你不能使用任何同名的东西。

【讨论】:

    【解决方案2】:

    在 C++ 中,语句

    AliAODEvent* aod = (This is where the object I want to point to goes);
    

    的意思是“请为我创建一个名为 aod 类型为 AliAODEvent* 的新变量,并为其分配一个特定变量。”另一方面,声明

    aod = (This is where the object I want to point to goes);
    

    表示“找到一个名为aod 的现有变量并将其分配给指向一个新位置。”这里的区别在于,通过包含类型的名称,C++ 认为您给出了一个引入新变量的 declaration,而不是一个采用现有变量并更改它指向的位置。这就是语言的设计方式。

    第二个有效而第一个无效的原因是,您最终是在尝试获取现有的东西并对其进行更改,这意味着您不应该提供声明。一般来说,只有在声明变量时才应在语句中包含变量的类型,否则应仅使用其名称。

    【讨论】:

      【解决方案3】:

      在这种情况下,您不会重新初始化指针,而是尝试创建同名的指针,这将是一个错误,除非它位于不同的范围块中。

      即:

      AliAODEvent* aod;
      
      {
         AliAODEvent* aod = (something)
      }
      

      在这种情况下,aod 变量会正常(尽管可能会显示警告),因为它是一个不同的变量,一旦它的范围('}')结束,它将不再存在。

      【讨论】:

        【解决方案4】:

        以下代码:

        A* aod = &x;
        A* aod = &y;
        

        声明了两个变量,并且格式不正确。它应该被大多数编译器拒绝,但如果设置正确,一些编译器可能会让它通过。

        这里发生的事情是编译器为第一个变量创建了存储,并将x的地址存储到其中。然后它为第二个变量创建附加存储并隐藏原始变量。

        此时,程序将xy 的值存储在不同的内存位置,但您只能引用第二个aod,直到它超出范围。

        这通常称为“阴影”,仅在作用域之间是合法的:

        #include <iostream>
        
        struct A {};
        
        A a, b, c;
        
        A* ptr = &a;
        
        void report(A* p) {
            std::cout << (void*) &a << ", " << (void*) &b << ", " << (void*) &c
                      << ": " << (void*) p << "\n";
        }
        
        int f() {
            std::cout << "f() ";
            report(ptr);
        }
        
        int main() {
            report(ptr);
            if (ptr != nullptr) {
                A* ptr = &b;
                report(ptr);
                if (ptr != nullptr) {
                    A* ptr = &c;
                    f();
                    report(ptr);
                }
                report(ptr);
            }
            report(ptr);
        }
        

        现场演示:http://ideone.com/LBFWFT

        另外,请注意指针是智能的。

        A* aod = new A;
        aod = new A;
        

        这分配了两个As,但从不释放第一个,因为我们从不告诉它,我们只是覆盖了值。编译器会跟踪动态对象何时不再被引用。

        int main() {
            A* aod = nullptr;
            if (aod == nullptr) {   // start new scope
                A x;
                aod = &x;
            }  // end scope, `x` is destroyed.
            use(aod);
        }
        

        此代码引入了一个错误:aod 指向x 的地址,但x 在超出} 的范围时被销毁,因此该指针为dangling 并引用无效内存。

        【讨论】:

          【解决方案5】:

          在给指针一个指向现有位置的地址时,您不想再次使用它左侧的类型说明符,因为该变量已经存在(即声明一个变量)。当您处于第一个 aod 的范围内时,您将无法再次声明新变量 aod

          另外,需要明确的是,aod=(Object to point to); 只有在赋值运算符之后有对象的地址 才是正确的,不是 对象本身。您可以通过在另一个对象的名称之前使用&amp; 运算符将对象的地址分配给指针以获取其地址:

          Object* objectPointer = &existingObject; //point objectPointer to the address of existingObject
          

          或者,通过一种方法,

          Object* objectPointer = method()
          

          其中method() 的返回类型为Object*(指向Object 类型的指针)。

          您还可以使用new 运算符在堆上创建一个新对象:

          Object* objectPointer = new Object("args"); 
          

          完成后必须使用delete 运算符将其删除。

          delete objectPointer;
          

          请记住,仅仅因为指针超出范围并停止存在,并不意味着它指向的内存存在。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-11-30
            • 2015-01-23
            • 2021-04-29
            • 1970-01-01
            • 1970-01-01
            • 2016-12-12
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多