c++ new的三种用法 【由基础到进阶 】

c++ new的三种用法 【由基础到进阶 】,第1张

new的三种用法
  • 1.运算符
    • 1.内置类型使用new
    • 2.自己设计的类型使用new
      • 计算创建创建对象的数量
      • 当我们使用new的时候,new和delete的使用一定要对应
    • 3.new分配失败之后便会抛出异常, 我们如果不想让它抛出异常的话,在new后面加上(nothrow)就可以了
  • 2.函数的用法
    • 与new 不同的点
    • 2.自己设计的类型使用函数方法
  • 3.定位new
    • 1内置类型使用定位new
    • 2.自己设计的类型使用定位new
      • 1.重定位对象
      • 2.重定位空间

1.运算符 1.内置类型使用new

int * ip1 = new int[5]{1,2,3,4,5};

编译器执行的步骤
1.先计算大小,为int类型,有5个则为20个字节
2.从内存中开辟20个字节空间大小
3.把这个空间初始化为1,2,3,4,5
4.把这个空间的地址赋值给ip1

2.自己设计的类型使用new
using namespace std;
class Complex {
private:
	int  real;
	int image;
public:
	Complex() :real{ 0 }, image{ 0 }{cout << "create default " << this << endl; }
	Complex(int Areal, int Aimage) :real{ Areal }, image{ Aimage } {cout << "create " << this << endl; }
	~Complex() { cout << "destory " << this << endl; }
	void Print() const {
		cout << "const  " << endl;

	}

	void show() {
		cout << "show" << endl;
		Print();
	}

};
int main() {
	Complex* ap = new Complex[5]{ {1,2},{3,4},{4,5} };
	delete[]ap;
}


此时我们可以看出,在上越界标志下面记录了对象的数量,
而只有 关键字new的这种用法在内存中才会有对象的数量

因此我们可以这样来

计算创建创建对象的数量

int n = * ((int * )ap - 1);

让我们来运行下面的代码验证一下


class Complex {
private:
	int  real;
	int image;
public:
	Complex() :real{ 0 }, image{ 0 }{cout << "create default " << this << endl; }
	Complex(int Areal, int Aimage) :real{ Areal }, image{ Aimage } {cout << "create " << this << endl; }
	~Complex() { cout << "destory " << this << endl; }
	void Print() {
		cout << real << " " << image << endl;
	}
};
int main() {
	Complex* ap = new(nothrow) Complex[]{ {1,2},{2,3} };
	int n = *((int*)ap - 1);
	for (int i = 0; i < n; ++i) {
		ap[i].Print();
	}
	delete[]ap;

}

结果如下

当我们使用new的时候,new和delete的使用一定要对应

new -->delete
new [] --> delete []

举个例子:
当这样创建一个对象时

int main() {
	Complex* ap = new Complex(1, 2);

	
}

就要这样释放 delete ap
千万不能写成这样 delete [] ap;
这样编译器会把上越界标志当成要析构的对象的数量,会析构cdcdcdcd个对象
代码运行结果如下

当这样创建一个对象时


int main() {
	Complex* ap = new Complex[5]{ {1,2},{2,3} };
	

}

释放的时候就要写成这样,delete [] ap;
千万不能写成这样delete ap,
这样的话编译器会只以为只析构一个对象,变会从上越界标志那里开始释放,这样就会造成异常

3.new分配失败之后便会抛出异常, 我们如果不想让它抛出异常的话,在new后面加上(nothrow)就可以了
Complex* ap = new(nothrow) Complex[5]{ {1,2},{2,3} };
	delete []ap;
2.函数的用法

与new 不同的点

1.需要手动计算字节大小
2.没有办法初始化
3.返回的地址为void * 类型,需要自己手动强转
1.内置类型使用函数方法

2.自己设计的类型使用函数方法
class Complex {
private:
	int  real;
	int image;
public:
	Complex() :real{ 0 }, image{ 0 }{cout << "create default " << this << endl; }
	Complex(int Areal, int Aimage) :real{ Areal }, image{ Aimage } {cout << "create " << this << endl; }
	~Complex() { cout << "destory " << this << endl; }
	void Print() {
		cout << real << " " << image << endl;
	}
};
int main() {
	Complex* ap = (Complex*) :: operator new(sizeof(Complex) * 5);
	for (int i = 0; i < 5; ++i) {
		new (&ap[i])  Complex(1, 2);
	}
	for (int i = 0; i < 5; ++i) {
		ap[i].Print();
	}
	for (int i = 0; i < 5; ++i) {
		ap[i].~Complex();
	}
	::operator delete (ap);
	ap = nullptr;
	
}

与new一样,它在分配失败时也会抛出异常
就上面的例子,我们不想让它抛出异常的话,则在后面加上 nothrow就可以了
Complex* ap = (Complex*) :: operator new(sizeof(Complex) * 5,nothrow);

3.定位new 1内置类型使用定位new


我们对开辟的空间重新定位,存储了3个int数据

2.自己设计的类型使用定位new 1.重定位对象
class Complex {
private:
	int  real;
	int image;
public:
	Complex() :real{ 0 }, image{ 0 }{cout << "create default " << this << endl; }
	Complex(int Areal, int Aimage) :real{ Areal }, image{ Aimage } {cout << "create " << this << endl; }
	~Complex() { cout << "destory " << this << endl; }
	void Print() {
		cout << real << "  " << image << endl;
	}
};
int main() {
	Complex * a =new Complex(1, 2);
	a->Print();
	new (a) Complex(2, 3);
	a->Print();

}

运行结果如下


我们可以看出,这个对象被重新构造了一次。

2.重定位空间

我们要记住一句话“有空间,不一定有对象,有对象,不一定有空间”。

class A {
private:
	int value;
public:
	A() {}
	A(int Avalue) :value{ Avalue } {
		cout << "create " << value << "   " << this << endl;
	}
	~A() {
		cout << "Destory " << value << "   " << this << endl;
	}
	 void Print() {
		cout << "Print " <<hex<< value << endl;

	}


};

int main() {
	A* a = (A*)malloc(sizeof(A));
	if (a == nullptr) {
		return 1;
	}
	a->Print();

	
}

让我们先运行一下观察一下它的结果。
发现打印的值为cdcdcdcd,为什么会这样呐?

这个是a->value的地址,因为在堆中开辟的空间,而堆中的空间默认都拿cdcdcdcd填充,而在调用Print函数的时候,编译器把a->value放到这块地址空间,并打印了出来

但是,我们要清楚,创建一个对象,必须要调用它的构造函数,此时它只是开辟了空间,始终没有创建对象,这样的话, 这个对象没有它的虚表和虚表指针,

在我们给Print函数前面加了virtual之后,它就会报错。


而此时,我们可以直接拿重定位new重新构建这个对象,这样就好了。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/920858.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-16
下一篇2022-05-16

发表评论

登录后才能评论

评论列表(0条)

    保存