C++初阶类和对象

Posted Huang_ZhenSheng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++初阶类和对象相关的知识,希望对你有一定的参考价值。

目录

一,构造函数

第一点:

第二点:

第三点:

第四点:

第五点:

二,析构函数

第一点:

第二点:

三,拷贝构造函数

第一点:


一,构造函数

下面先看一段代码:

class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
class Stack
{
public:
	void Init()
	{
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	Date d1;
	d1.Init(2021,10,9);
	Stack s1;
	s1.Init();
	return 0;
}

经常会忘记调用Init函数

能否做到对象定义出来就初始化了呢?

————>构造函数,在对象定义的时候就去调用

构造函数是特殊的成员函数,需要注意的是,构造函数的虽然名称叫构造,但是需要注意的是构造函数的主要任务并不是开空间创建对象,而是初始化对象

第一点:

1. 函数名与类名相同

2. 无返回值

3. 对象实例化时编译器自动调用对应的构造函数

4. 构造函数可以重载

函数重载:可以提供多种初始化对象的方式

例如:

class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date()
	{
		_year = 0;
		_month = 1;
		_day = 1;
	}
	Date(int year,int month,int day)
	{
		_year = year ;
		_month = month ;
		_day = day ;
	}
private:
	int _year;
	int _month;
	int _day;
};
class Stack
{
public:
	void Init()
	{
		_a = nullptr;
		_top = _capacity = 0;
	}
	Stack()
	{
		_a = nullptr;
		_top = _capacity = 0;
	}
	Stack(int capacity)
	{
		_a = (int*)malloc(sizeof(int)*capacity);
		if (_a == nullptr)
		{
			cout << "malloc fail" << endl;
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	Date d1;
	Date d2(2021,10,9);
	//d1.Init(2021,10,9);
	Stack s1;
	Stack s2(10);
	//s1.Init();
	return 0;
}

第二点:

如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。

 我们不写构造函数,编译器默认生成构造函数,做了一个偏心的处理

1.对于内置类型(int,char,double,指针)不会初始化

2.对于自定义类型(struct/class),会调用它的构造函数初始化

例如: 

class A
{
public:
	A()
	{
		_a1 = 0;
		_a2 = 1;
	}
private:
	int _a1;
	int _a2;
};
class Date
{
public:
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	//内置类型/基本类型    int,char,double,指针......
	int _year;
	int _month;
	int _day;
	//自定义类型
	A _aa;
};
int main()
{
	Date d1;
	d1.Print();
	return 0;
}

 自定义类型它会调用它的无参构造函数初始化

下面继续看一段代码: 

class A
{
public:
	A(int a)
	{
		_a1 = 0;
		_a2 = 1;
		cout << "A()" << endl;
	}
private:
	int _a1;
	int _a2;
};
class Date
{
public:
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	//内置类型/基本类型    int,char,double,指针......
	int _year;
	int _month;
	int _day;
	//自定义类型
		A _aa;
};
int main()
{
	Date d1;
	d1.Print();
	return 0;
}

C++的时候,语法委员会为这里打了一个补丁

 成员变量只是定义,这里不是初始化,这里就像函数缺省参数一样,给的是缺省值

第三点:

无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数。

默认构造函数:不传参数就可以调用的那个,无参或者全缺省的都可以叫做默认构造函数

下面这段代码 本来是构成函数重载的,但是他们两个不能同时存在,Date d会出现歧义,编译器不知道调谁了

class Date
{
public:
	//本来是构成函数重载的
	//但是他们两不能同时存在!!!
	Date()
	{
		_year = 0;
		_month = 1;
		_day = 1;
	}
	Date(int year = 0,int month = 1,int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date(1, 1, 1);
	return 0;
}

 一般情况下推荐全缺省的构造函数

第四点:

关于编译器生成的默认成员函数,很多童鞋会有疑惑:在我们不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象year/month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么卵用??

————>

解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语法已经定义好的类型:如int/char...,自定义类型就是我们使用class/struct/union自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数

第五点:

 以上这段代码的year为随机值,局部变量优先,自己给自己,这个变量压根没动到

改成下面这样就可以通过

二,析构函数

析构函数是特殊的成员函数

第一点:

1. 析构函数名是在类名前加上字符 ~。
2. 无参数无返回值。
3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

class Date
{
public:
	Date(int year = 1,int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
	//像Date这样的类是不需要析构函数的,因为他内部没有什么资源需要清理
	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
class Stack
{
public:
	Stack(int capacity = 4)
	{
		_a = (int*)malloc(sizeof(int)*capacity);
		if (_a == nullptr)
		{
			cout << "malloc fail" << endl;
		}
		_top = 0;
		_capacity = capacity;
	}
	~Stack()
	{
		//像Stack这样的类,对象中的资源需要清理,就用析构函数
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	Date d1;
	Stack st;
	return 0;
}

第二点:

关于编译器自动生成析构函数,编译器生成的默认析构函数,对会自定类型成员调用它析构函数。

三,拷贝构造函数

构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

特征:

1. 拷贝构造函数是构造函数的一个重载形式
2. 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2021,10,9);
	Date d2(d1);
	d2.Print();
	return 0;
}

 如果引用传参,不是做输出型参数,最好用const & 做保护

第一点:

 我们不写,编译器会默认生成拷贝构造,跟构造和析构又不太一样

不会去区分内置类型和自定义类型成员,都会处理

 1,内置类型,字节序的浅拷贝

2,自定义类型,会去调用他的拷贝构造完成拷贝

那么编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了(内置类型和自定义类型,默认生成的拷贝构造都处理了),是否意味着,编译器自己生成就OK呢?

class Stack
{
public:
	Stack(int capacity = 4)
	{
		_a = (int*)malloc(sizeof(int)*capacity);
		if (_a == nullptr)
		{
			cout << "malloc fail" << endl;
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	~Stack()
	{
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	Stack st1;
	Stack st2(st1);
	return 0;
}

上面这段代码崩了?why?

在进行析构的时候,st2先析构, 把空间给释放了,将_a给置成了NULL,_top置为0,_capacity置为0,free(a)崩溃(同一个空间不能释放两次),导致同一块空间释放两次

编译器默认生成的拷贝构造并不能解决所以的问题,像Stack这样类,编译器默认生成拷贝构造完成的就是浅拷贝。

解决方案:自己实现深拷贝拷贝构造函数!

以上是关于C++初阶类和对象的主要内容,如果未能解决你的问题,请参考以下文章

C++ 初阶类和对象

C++ 初阶类和对象

C++初阶类和对象

C++初阶类和对象

C++初阶---类和对象

C++初阶第四篇——类和对象(上)(类的定义+封装+this指针)