• 一文详解C++动态对象创建


     当我们创建数组的时候,总是需要提前预定数组的长度,然后编译器分配预定长度的数组空间,在使用数组的时候,可能数组的空间不太大,浪费空间,也许空间太大了,浪费空间,也许空间不足,所以对于数组来说,如果能够根据需要分配空间大小再好不过。

     C语言中很好的提供了动态分配内存,malloc和free可以在运行时从堆中分配存储单元

    一、C语言创建和销毁堆区空间出现的问题

    • 程序员必须确定对象的长度
    • malloc返回一个void* 指针,C++不允许将void*赋值给其他指针,必须进行强制转换
    • malloc可能申请内存失败,所以必须判断返回值来确保内存分配成功
    • 用户在使用对象之前必须记住对他初始化,构造函数不能显示调用初始化(构造函数都是由编译器进行调用的),用户有可能忘记调用初始化函数

    对于最后一点,案例如下:

    #include
    using namespace std;
    #define _CRT_SECURE_NO_WARNINGS
    #pragma warning(disable:4996)
    
    class Maker {
    
    public:
    	// explicit 关键字只能放在构造函数前面 构造函数只有一个参数或者其他参数有默认值时
    	// 防止编译器优化 Maker m = 10; 这种格式
    	Maker()
    	{
    		
    		cout << "调用构造函数" << endl;
    	}
    
    	~Maker()
    	{
    		cout << "调用法国析构函数" << endl;
    	}
    
    private:
    	int id;
    
    };
    
    
    void test01()
    {
    	// 使用C语言方式申请堆区空间  不会调用构造函数 对象释放也不会调用析构函数
    	Maker* m = (Maker*)malloc(sizeof(Maker));
        free(m);
    }
    
    int main()
    {
    	//Maker m = 1;//err
    	return EXIT_SUCCESS;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    二、C++创建和销毁堆区空间

    C++解决动态内存分配的方案是把创建一个对象所需要的操作都结合在一个new的运算符中,当使用New创建一个对象时,它就在堆中为对象分配内存并且调用构造函数完成初始化

    Person* person = new Person;
    
    相当于:
    Person* person = (Person*)malloc(sizeof(Person));
    
    if(person == NULL)
    {
        return 0;
    }
    
    person->init();
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    案例代码:创建一个无参构造对象

    #include
    using namespace std;
    #define _CRT_SECURE_NO_WARNINGS
    #pragma warning(disable:4996)
    
    class Maker {
    
    public:
    	// explicit 关键字只能放在构造函数前面 构造函数只有一个参数或者其他参数有默认值时
    	// 防止编译器优化 Maker m = 10; 这种格式
    	Maker()
    	{
    		cout << "调用构造函数" << endl;
    	}
    
    	~Maker()
    	{
    		cout << "调用析构函数" << endl;
    	}
    
    private:
    	int id;
    
    };
    
    void test02()
    {
    	// 用New方式申请堆区空间  会调用类的构造函数
    	Maker* m = new Maker;// 这种方式调用的是无参构造函数
    	delete m;
        m = NULL;
    }
    
    
    void test01()
    {
    	// 使用C语言方式申请堆区空间  不会调用构造函数 对象释放也不会调用析构函数
    	Maker* m = (Maker*)malloc(sizeof(Maker));
    	free(m);
    }
    
    int main()
    {
    	//Maker m = 1;//err
    	test02();
    	return EXIT_SUCCESS;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    案例代码:创建一个有参数构造对象

    #include
    using namespace std;
    #define _CRT_SECURE_NO_WARNINGS
    #pragma warning(disable:4996)
    
    class Maker {
    
    public:
    	// explicit 关键字只能放在构造函数前面 构造函数只有一个参数或者其他参数有默认值时
    	// 防止编译器优化 Maker m = 10; 这种格式
    	Maker(int a)
    	{
    		cout << "调用构造函数" << endl;
    		id = a;
    	}
    
    	~Maker()
    	{
    		cout << "调用析构函数" << endl;
    	}
    
    private:
    	int id;
    
    };
    
    void test02()
    {
    	// 用New方式申请堆区空间  会调用类的构造函数
    	Maker* m = new Maker(1);
    	delete m;
    }
    
    
    void test01()
    {
    	// 使用C语言方式申请堆区空间  不会调用构造函数 对象释放也不会调用析构函数
    	Maker* m = (Maker*)malloc(sizeof(Maker));
    	free(m);
    }
    
    int main()
    {
    	//Maker m = 1;//err
    	test02();
    	return EXIT_SUCCESS;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    总结:

    • 为什么要有新的方法申请和释放堆区空间?
      • 因为C语言的方式不会调用构造和析构函数
    • C++中申请堆区空间和释放堆区空间用什么关键字
      • 申请堆区空间使用new,释放堆区空间使用delete

    三、new和delete用于申请基本类型的数组

    #include
    using namespace std;
    #define _CRT_SECURE_NO_WARNINGS
    #pragma warning(disable:4996)
    
    // new 创建基础类型的数组
    void test01()
    {
    	// 申请基础数据类型的数组
    	int* pInt = new int[10]{1,2,3,4,5,6,7,8,9,10};
    	for (int i = 0; i < 10; i++)
    	{
    		//pInt[i] = i + 1;  这样写也可以
    		*(pInt + i) = i + 1;
    	}
    
    	for (int i = 0; i < 10; i++)
    	{
    		cout << pInt[i] << " ";
    	}
    
    	cout << endl;
    
    
    	char* pChar = new char[64];
    	memset(pChar,0,sizeof(char) * 64);
    	strcpy(pChar,"小花");
    	cout << pChar << endl;
    
    	// 释放内存空间
    	delete[] pInt;
    	delete[] pChar;
    }
    
    int main()
    {
    	//Maker m = 1;//err
    	test01();
    	return EXIT_SUCCESS;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    四、new创建对象数组

    #include
    using namespace std;
    #define _CRT_SECURE_NO_WARNINGS
    #pragma warning(disable:4996)
    
    class Maker {
    public:
    	Maker()
    	{
    		cout << "构造函数" << endl;
    	}
    
    	~Maker()
    	{
    		cout << "析构函数" << endl;
    	}
    };
    
    // 创建对象类型的数组
    void test02()
    {
    	Maker* ms = new Maker[2];// 调用无参构造  申请两个对象
    
    	delete[] ms;
    }
    
    int main()
    {
    	//Maker m = 1;//err
    	test02();
    	return EXIT_SUCCESS;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    五、delete void * 可能会出错,不会调用对象的析构函数

    #include
    using namespace std;
    #define _CRT_SECURE_NO_WARNINGS
    #pragma warning(disable:4996)
    
    class Maker {
    public:
    	Maker()
    	{
    		cout << "构造函数" << endl;
    	}
    
    	~Maker()
    	{
    		cout << "析构函数" << endl;
    	}
    };
    
    // 创建对象类型的数组
    void test02()
    {
    	Maker* ms = new Maker[2];// 调用无参构造  申请两个对象
    
    	delete[] ms;
    }
    
    // delete void* 可能会出错  不会调用对象的析构函数
    void test03()
    {
    	void* m = new Maker;// 无参构造申请一个对象
    	// 如果使用void * 来接new的对象  那么delte时不会调用析构函数
    	delete m;
    }
    
    int main()
    {
    	//Maker m = 1;//err
    	test03();
    	return EXIT_SUCCESS;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    #include
    using namespace std;
    #define _CRT_SECURE_NO_WARNINGS
    #pragma warning(disable:4996)
    
    class Maker {
    public:
    	Maker()
    	{
    		cout << "构造函数" << endl;
    	}
    
    	~Maker()
    	{
    		cout << "析构函数" << endl;
    	}
    };
    
    
    // delete void* 可能会出错  不会调用对象的析构函数
    void test03()
    {
    	void* m = new Maker;// 无参构造申请一个对象
    	// 如果使用void * 来接new的对象  那么delte时不会调用析构函数
    
    	// 在编译阶段  那么编译器就确定好了函数的调用地址
    	// C++编译器不认识void* 不知道void* 指向哪一个函数  所以会调用析构函数
    	// 这种编译方式叫做静态联编
    	delete m;
    }
    
    int main()
    {
    	//Maker m = 1;//err
    	test03();
    	return EXIT_SUCCESS;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    六、C和C++的申请和释放堆区空间不要混用

    错误写法:

    void test04()
    {
    	Maker *m = new Maker;//c++
    
    	free(m);//c
    	
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    c++和C的共同和区别之处:

    共同:都是申请堆区空间和释放堆区空间

    区别:C++会调用构造和析构函数

  • 相关阅读:
    jdbc 技术执行 insert 后获取自增列的值,插入操作时获取自增列的值
    炫酷爱心表白
    RockyLinux安装MariaDB
    QT---lineEdit相关信号
    k8s 服务升级为啥 pod 会部署到我们不期望的节点上??看来你还不懂污点和容忍度
    【精通内核】Linux内核自旋锁实现原理与源码解析
    Splashtop 与 Canopy 携手共同增强对物联网设备的远程管理
    欢度盛夏,畅享清凉——七月超市营销策略
    (十四)OpenCV中的自带颜色表操作cv::LUT
    我用这10招,能减少了80%的BUG
  • 原文地址:https://blog.csdn.net/qq_44653420/article/details/126914666