重生之c++学习:类与对象进阶1

  • 构造函数
  • 析构函数
  • 拷贝构造函数

上一节我们引用了一个栈的类但实际上栈的类初始化和销毁,依靠着构造函数和析构函数进行,没有必要额外写个init()和destory(),我们通过c++类与对象的语法用这构造、析构函数来表现

class stack {

public:

	// 栈的初始化  ---> 构造函数 // 创造对象的时候自动调用
	stack() {

		_stackArray = nullptr;
		_stackTop = _capacity = 0;
	}
	~stack() {
		
		_stackArray = nullptr;
		_stackTop = _capacity = 0;
	}
	void push(int value) {
		// 扩容
		if (_stackTop == _capacity) {
			
			int newCapacity = _capacity == 0 ? 4: _capacity * 4 ;
			int* tmp = (int*)realloc(_stackArray, sizeof(int) * newCapacity);
			if (tmp == nullptr) {

				perror("malloc fail");
				return;
			}
			
			_stackArray = tmp;
			_capacity = newCapacity;
		}
		// 栈顶插入
		_stackArray[_stackTop] = value;
		_stackTop++;
	}
	void pop() {
		if (_stackTop == 0) {

			cout << " 栈内没有元素可以出栈 " << endl;
			return;
		}

		_stackTop--;
	}
	void printTop() {

		assert(_stackTop!=0);
		cout << _stackArray[_stackTop-1] << endl;
	}

	// 成员变量最好加前杠(利于成员方法区别参数),成员变量一般封装
private:

	int* _stackArray;
	int _capacity;
	int _stackTop;

};
void test_1() {

	stack myStack ; // 创造对象的同时进行析构函数
	myStack.push(10);
	myStack.printTop();
}
// test_1这个生命周期的结束 自动走析构函数

那么开始讲一下构造函数(重点)和析构函数

构造函数

构造函数的重载

这里上一段错误的代码(这个无参数的构造函数会跟这个全缺省在test_2()中无参数调用时无法区分

class Data{

public:

    Data(){
    
        _year = 1;
        _month = 1;
        _day = 1;
        
    }
       
    Data(int year = 1; int month = 1; int day = 1){

        _year = year;
        _month = month;
        _day = day;
    }
    
private:
    
    int _year;
    int _month;
    int _day;
    const int _num;
};

void test_2(){

    Data today; // 因为在这个类里面 这两个构造函数会冲突,不知道选哪一个
}

 那么对于这种类我们可以只用全缺省构造函数,这里也体现了全缺省的优势

class Data{

public:

       
    Data(int year = 1; int month = 1; int day = 1){

        _year = year;
        _month = month;
        _day = day;
    }
    
private:
    
    int _year;
    int _month;
    int _day;
    const int _num;
};

void test_3(){
    
    // 兼顾无参数传入 
    Data today;
    // 也可以直接传入
    Data tomorrow(2023, 7, 23);
    
}

构造函数的机制

 但是在构造函数中有一个 “初始化列表”

初始化列表

初始化列表是每个成员定义的地方,每个成员都需要走,内置类型走默认构造,自定义类型走自定 ,然后在C++11版本开始封装区声明时的缺省值,是传给初始化列表,顺序仅与声明顺序有关

// 构造函数
Date(int year, int month, int day)
    
    : _year(year)
    , _month(month)
    , _day(day)
    , _num(10)    // 这里的10是给const修饰的_num复制为10
    
{
    // 如果这里 _num = 10; 会报错,因为const修饰一旦生成就无法修改,进入构造函数在就已修改

}

实际上,初始化列表一般用于在没有自定义类型的构造函数的时候,用于给自定义类型来进行构造的地方,下面用队列来演示

class myQueue {

public:

	myQueue(int capacity)
    
    : _pushStack(capacity)
    , _popStack(capacity)
{
    
}


private:

	Stack _pushStack;
	Stack _popStack;
 
};

接着就是我们的析构函数了

析构函数

与构造函数不同的是析构函数就只有一个,完成对象在生命周期结束前的销毁、释放内存的操作,每一个类中都应该析构函数

关于自定义类型处理的问题

在构造函数和析构函数中,我们定义类型的时候,属于c++内置类型,例如:int / char / double /.....这些时,c++编译器不进行处理,如果是自定义的成员变量(如结构体成员),那编译器就会去生成处理,向下回去处理该成员的默认构造函数,那么到了这里构造函数和析构函数的学习就结束啦

拷贝构造函数

拷贝构造就是把对象B与对象A弄得一样的操作,需要注意的是传入引用,传入拷贝的话会死循环

 下面是一个示例,演示如何定义和使用拷贝构造函数:

浅拷贝
#include <iostream>
using namespace std;

class MyClass {
private:
    int data;
public:
    // 默认构造函数
    MyClass() {
        cout << "Default constructor called" << endl;
        data = 0;
    }

    // 带参数构造函数
    MyClass(int value) {
        cout << "Parameterized constructor called" << endl;
        data = value;
    }

    // 拷贝构造函数
    MyClass(const MyClass& obj) {
        cout << "Copy constructor called" << endl;
        data = obj.data;
    }

    // 成员函数
    void display() {
        cout << "Data: " << data << endl;
    }
};

int main() {
    MyClass obj1(10);  // 调用带参数构造函数
    MyClass obj2(obj1);  // 调用拷贝构造函数
    obj1.display();  // 输出: Data: 10
    obj2.display();  // 输出: Data: 10

    return 0;
}

在上面的示例中,我们定义了一个名为MyClass的类,包含了默认构造函数、带参数构造函数和拷贝构造函数。在main函数中,我们首先使用带参数构造函数创建了一个对象obj1,并将其值设置为10。然后,我们使用拷贝构造函数创建了另一个对象obj2,并将其内容初始化为obj1。最后,我们分别调用对象的display函数,输出了它们的数据值。

需要注意的是,如果没有显式定义拷贝构造函数,编译器会自动生成一个默认的拷贝构造函数。但在某些情况下,例如类中包含指针成员变量时,需要自定义拷贝构造函数来确保正确地复制指针所指向的内存。

上面的是拷贝构造函数的浅拷贝(直接指向同一块空间),接着补充深拷贝(另开一片空间

深拷贝
	// 栈的深拷贝
	stack(stack& myStack) {

		_stackArray = (int*)malloc(sizeof(int) * myStack._capacity);
		if (_stackArray == NULL) {

			perror("malloc Fail");
			return;
		}

		// 对象数据的拷贝
		memcpy(_stackArray, myStack._stackArray, sizeof(int) * myStack._stackTop);
		_stackTop = myStack._stackTop;
		_capacity = myStack._capacity;
	}

 这里简略了data的拷贝函数(自己去写很简单的),所以总结以下

1.内置类型直接就浅拷贝就好,也就是默认生成

2.自定类型涉及了开辟空间和数据拷贝,进行深拷贝,但是当自定义类型中分出了自定类型,比如栈实现队列时,只要栈里面存在深拷贝,就没必要给队列写一个深拷贝,直接默认生成拷贝函数就可解决

3.尽量拷贝的时候写一个const在传入引用前保证传入的拷贝的对象不受影响

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值