#include<bits/stdc++.h>
using namespace std;
class Array{
	private:
		int n;//数组大小 
		int *a;//数组 
	public:
		// write your code here......
        Array(){
			cin >>n;
			a = new int[n];
			for(int i=0; i<n; i++){
				cin>>a[i];
			}

		}
		~Array(){
			delete []a;
		}
		void show(){
			for (int i=0;i<n;i++) cout<<a[i]<<' ';
		}
};
int main(){
	Array a;
	a.show();
	return 0;
}


你的两段代码核心差异是 「变量遮蔽(Name Hiding)」 —— 第二段构造函数里定义了局部变量 int* a,覆盖了类的成员变量 int* a,导致成员变量未被正确初始化,进而引发编译/运行错误(编译器可能不报错,但运行时必崩溃,你感知的“不正常编译”本质是运行逻辑错误)。

一、先拆解两段代码的核心区别

第一段(正常):给「成员变量a」赋值

Array(){
    cin >>n;
    a = new int[n]; // 这里的a是类的成员变量 int* a
    for(int i=0; i<n; i++){
        cin>>a[i]; // 给成员变量a指向的动态数组赋值
    }
}

  • 构造函数里的 a 没有重新定义,直接指向类的成员变量int* a
  • 执行 a = new int[n] 后,成员变量 a 指向一块动态分配的数组内存;
  • 后续 cin>>a[i] 给这块内存赋值,show() 能正常访问,析构函数 delete[]a 也能正确释放内存。

第二段(异常):定义了「局部变量a」,遮蔽成员变量

Array(){
    cin >>n;
    int* a = new int[n]; // 这里定义了【局部变量】int* a,和成员变量同名
    for(int i=0; i<n; i++){
        cin>>a[i]; // 给局部变量a指向的数组赋值
    }
}

这行 int* a = new int[n]; 是关键错误:

  • 它在构造函数的局部作用域里,定义了一个新的指针变量 a,和类的成员变量 int* a 同名;
  • 编译器遵循「局部优先」原则:构造函数里所有的 a 都会被识别为局部变量a,而非成员变量a;
  • 构造函数执行完毕后,局部变量a会被销毁(出作用域),但它指向的动态数组内存没有被释放(内存泄漏);
  • 类的成员变量 a 从头到尾都是「空指针(nullptr)」(未被赋值)。

二、为什么第二段“不正常”?(运行时必崩溃/报错)

虽然第二段代码语法上没有编译错误(编译器不会提示语法问题),但运行时会触发两个致命问题:

问题1:show() 函数访问空指针,触发崩溃

show() 里的 a[i] 访问的是成员变量a(空指针),对空指针解引用(a[i] 等价于 *(a+i))是C++的「未定义行为」,直接导致程序崩溃(比如段错误、闪退)。

问题2:析构函数 delete[]a 释放空指针(或野指针)

  • 如果成员变量a是空指针:C++11后 delete[] nullptr 是安全的,但局部变量a指向的动态数组内存永远无法释放(内存泄漏);
  • 如果成员变量a是随机野指针(未初始化的指针值):delete[] 野指针会直接崩溃。

三、直观对比:两段代码的内存状态

构造函数执行时

成员变量a → 指向动态数组(内存地址0x123)

局部变量a → 指向动态数组(0x456);成员变量a → 空指针(0x0)

构造函数执行结束

成员变量a 保留指向0x123

局部变量a销毁,0x456内存泄漏;成员变量a仍为0x0

show()调用时

访问0x123的数组,正常输出

访问0x0的空指针,崩溃

析构函数执行时

释放0x123的内存,正常

释放0x0的空指针,无意义且内存泄漏

四、修正第二段代码的两种方式

核心是「让构造函数给成员变量a赋值,而非定义局部变量」:

方式1:去掉局部变量的定义(推荐,和第一段一致)

Array(){
    cin >>n;
    a = new int[n]; // 直接给成员变量a赋值,不重新定义
    for(int i=0; i<n; i++){
        cin>>a[i];
    }
}

方式2:用 this->a 明确指定成员变量(避免遮蔽)

如果想保留局部变量名(不推荐,易混淆),可以用 this-> 区分成员变量:

Array(){
    cin >>n;
    int* a = new int[n]; // 局部变量a
    for(int i=0; i<n; i++){
        cin>>a[i]; // 给局部a赋值
    }
    this->a = a; // 把局部a的地址赋值给成员变量a(此时成员a指向有效内存)
}

⚠️ 注意:这种方式虽然能运行,但没必要定义局部变量,徒增复杂度,优先用方式1。

五、总结:变量遮蔽的核心坑点

  1. 局部变量会覆盖同名的成员变量:编译器优先访问“更小作用域”的变量,这是C++的作用域规则;
  2. 指针未初始化=空指针/野指针:类的成员变量如果是指针,默认不会初始化为nullptr(全局对象除外),未赋值的指针是随机野指针,访问/释放都会崩溃;
  3. 动态内存要“谁分配谁释放”:局部指针指向的动态内存,若没有赋值给成员变量,出作用域后会内存泄漏。

你的第二段代码本质是「语法合法,但逻辑错误」,这也是新手最容易踩的坑 —— 写代码时要注意:避免局部变量和成员变量同名,如果必须同名,用 this->成员名 明确指定成员变量。