C++:类和对象(3)

目录

1.构造函数调用规则

2.深拷贝和浅拷贝

3.初始化列表

4.类对象作为类成员


1.构造函数调用规则

     默认情况下,C++编译器至少给类添加三个函数:

1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝构造函数,对属性进行值的拷贝

     构造函数调用规则如下:

如果用户自己实现了有参构造函数,C++不会再提供无参构造函数,但是会提供默认拷贝构造

如果用户自己实现了拷贝构造函数,C++不会提供任何构造函数

     比如:

     1.如果用户自己实构造函数,C++不会再提供无参构造函数,但是会提供默认拷贝构造

#include
using namespace std;
class Person
{
public:
	//有参构造函数
	Person(int age)
	{
		_age = age;
		cout << "有参构造函数调用" << endl;
	}
	//析构函数
	~Person()
	{
		cout << "析构函数调用" << endl;
	}
	//拷贝构造函数
	Person(const Person& p)
	{
		_age = p._age;
		cout << "拷贝构造函数调用" << endl;
	}

	int _age;

};
int main()
{
	Person p;
	p._age = 18;

	Person p2(p);

	cout << "p2的年龄为:" << p2._age << endl;

	return 0;
}

     此时会显示报错:

类 "Person" 不存在默认构造函数

     因为我们写下了Person p,没有参数,要调用系统或者用户提供的无参构造函数,但因为我们实现了有参构造函数,系统就不会提供无参构造函数,同时我们自己也没有实现无参构造函数,就会报错。

      还是会提供默认构造,如果我们把Person p和拷贝构造函数代码删掉:

#include
using namespace std;
class Person
{
public:
	//有参构造函数
	Person(int age)
	{
		_age = age;
		cout << "有参构造函数调用" << endl;
	}
	//析构函数
	~Person()
	{
		cout << "析构函数调用" << endl;
	}

	int _age;

};
int main()
{
	
    Person p(18);

	Person p2(p);

	cout << "p2的年龄为:" << p2._age << endl;

	return 0;
}

     运行结果:

有参构造函数调用
p2的年龄为:18
析构函数调用
析构函数调用

      可以看到,系统会提供默认拷贝构造

2.如果用户自己实现了拷贝构造函数,C++不会提供任何构造函数

#include
using namespace std;
class Person
{
public:
	//拷贝构造函数
	Person(const Person& p)
	{
		_age = p._age;
		cout << "拷贝构造函数调用" << endl;
	}
	//析构函数
	~Person()
	{
		cout << "析构函数调用" << endl;
	}
	

	int _age;

};
int main()
{
	Person p;

	return 0;
}

     报错:

“Person”: 没有合适的默认构造函数可用

     可以看出,当我们自己实现了拷贝构造函数,如果我们自己不实现无参构造函数,系统就不会提供,有参构造函数系统本来就提供不了。

2.深拷贝和浅拷贝

     浅拷贝:简单的赋值拷贝操作

     深拷贝:在堆区重新申请空间,进行拷贝操作

#include
using namespace std;
class Person
{
public:
	//无参构造函数
	Person()
	{
		cout << "无参构造函数" << endl;
	}
	//有参构造函数
	Person(int age,int height)
	{
		_age = age;
		_height=new int(height);
		cout << "有参构造函数调用" << endl;
	}
	//析构函数
	~Person()
	{
		//将堆区开辟的数据做释放操作
		if (_height != NULL)
		{
			delete _height;
			_height = NULL;
		}
		cout << "析构函数调用" << endl;
	}

	int _age;
	int* _height;
};
int main()
{
	Person p1(18,180);

	cout << "p1的年龄为:" << p1._age <<"身高为:"<<*p1._height<

     我们想把身高这个数据开辟在堆区,就可以用new语法来创建,new创建之后返回一个指向该空间的指针,用_height来接收,同时堆区开辟的空间需要我们程序员手动来释放,上面给出了代码,运行看看怎么样,不好,出错了,这时候会出现报错:

已在 Project15.exe 中执行断点指令(__debugbreak()语句或类似调用)。

     这是为什么呢?其实是因为我们利用编译器提供的拷贝构造函数,做的是浅拷贝的操作

C++:类和对象(3)_第1张图片

     p1和p2的指针都指向同一块空间,同一块空间释放两次肯定是不允许的,所以报错意料之中,浅拷贝带来的问题就是堆区的内存重复释放,要用深拷贝来解决

#include
using namespace std;
class Person
{
public:
	//无参构造函数
	Person()
	{
		cout << "无参构造函数" << endl;
	}
	//有参构造函数
	Person(int age,int height)
	{
		_age = age;
		_height=new int(height);
		cout << "有参构造函数调用" << endl;
	}
	//自己实现拷贝构造函数,解决浅拷贝问题
	Person(const Person& p)
	{
		_age = p._age;
        //_height=p._height;编译器提供的拷贝构造函数实现这段代码,就是直接赋值
		_height = new int(*p._height);
        cout<<"拷贝构造函数调用"<

     自己再开辟一块堆区空间,将指针指向这块空间,释放的时候就不会再重复释放空间了。

3.初始化列表

     初始化列表语法用来初始化属性

语法:构造函数():属性1(值1),属性2(值2)...{}

     示例:

     传统的初始化操作:

class Person
{
public:
	Person(int a, int b, int c)
	{
		_a = a;
		_b = b;
		_c = c;
	}
	int _a;
	int _b;
	int _c;
};

     初始化列表操作:

#include
using namespace std;
class Person
{
public:
	Person(int a, int b, int c):_a(a),_b(b),_c(c)
	{

	}
	int _a;
	int _b;
	int _c;
};
int main()
{
	Person p(10, 20, 30);
	cout << "_a=" << p._a << endl;
	cout << "_b=" << p._b << endl;
	cout << "_c=" << p._c << endl;
	
	return 0;
}

     输出:

_a=10
_b=20
_c=30

     使用初始化列表,函数体就可以写其他的内容,很灵活。

4.类对象作为类成员

    C++中类的成员可以是另一个类的对象,这种成员叫对象成员   

    比如:

class A{};
class B
{
   A a;
};

     那我们就有问题了,当我们创建B的对象时,A和B的构造函数和析构函数先执行谁的呢?让我们一起来看看

#include
using namespace std;
class Phone
{
public:
	Phone(string Pname)
	{
		_Pname = Pname;
		cout << "Phone的构造函数调用" << endl;
	}
	~Phone()
	{
		cout << "Phone的析构函数调用" << endl;
	}
	string _Pname;
};
class Person
{
public:
	Person(string name, string Pname):_name(name),_Phone(Pname)
	{
		cout << "Person的构造函数调用" << endl;
	}
	~Person()
	{
		cout << "Person的析构函数调用" << endl;
	}
	string _name;//姓名
	Phone _Phone;//手机
};
int main()
{
	Person p("张三", "华为Mate60pro");
	cout << p._name << "拿着" << p._Phone._Pname << endl;
	return 0;
}

      输出:

Phone的构造函数调用
Person的构造函数调用
张三拿着华为Mate60pro
Person的析构函数调用
Phone的析构函数调用

     可以看到,当其他对象作为本类对象时,构造时先构造其他对象,再构造本类对象;析构的顺序是先调用本类对象的析构函数,再调用其他对象的析构函数。

你可能感兴趣的:(c++)