一、编译器生成的成员函数
1、默认构造函数(没有参数,或者参数有默认值),如果派生类的成员初始化列表没有显式的调用基类的构造函数,编译器会调用基类的默认构造函数。如果定义了构造函数,那么编译器将不会定义默认构造函数,如果需要默认构造函数,需要自己定义。
2、复制构造函数(拷贝构造函数),复制构造函数接受其所属类的对象作为参数
格式:
3、赋值运算符
默认的赋值运算符用于处理同类对象之间的赋值,注意区分赋值与初始化
编译器提供的默认赋值是成员赋值,如果成员是类对象,那么默认成员赋值将调用相应的赋值运算符。
如果需要显式定义复制构造函数,则基于相同的原因,需要显式定义赋值运算符
原型如下:
编译器不会生成将一种类型赋值给另一种类型的赋值运算符,如果需要
显式定义函数,重载=运算符
二、常见的类方法和操作
1、通常情况下,编写使用对象作为参数的函数时,应该按引用而不是按值来传递对象。这样做的原因是提高效率。按值传递涉及到生成参数的临时拷贝,即调用复制构造函数,然后调用析构函数删除临时拷贝。如果函数不修改对象,应将参数声明为const引用
2、 另一个原因是,当继承的基类有虚函数时,派生类的函数如果参数是基类对象的引用,那么在定义函数时可以传递派生类的对象
3、返回对象和返回引用
注意:如果函数 返回在函数中创建的临时对象,则不要返回引用
三、公有继承
使用公有继承要考虑的因素:
1、什么不能被继承?
构造函数,析构函数,赋值运算符,友元函数
2、如果派生类希望使用基类的友元函数,如何解决?
3、析构函数,通常情况下应该是虚的
4、对于派生类而言,保护的成员类似公有成员,对于外部而言,保护成员类似于私有成员。
派生类可以直接访问基类的保护成员
1、默认构造函数(没有参数,或者参数有默认值),如果派生类的成员初始化列表没有显式的调用基类的构造函数,编译器会调用基类的默认构造函数。如果定义了构造函数,那么编译器将不会定义默认构造函数,如果需要默认构造函数,需要自己定义。
2、复制构造函数(拷贝构造函数),复制构造函数接受其所属类的对象作为参数
格式:
classname(const classname& cn);
什么时候使用复制构造函数:
- 将新对象初始化为一个同类对象
- 按值将对象传递给函数
- 函数按值返回对象
- 编译器生成临时对象
3、赋值运算符
默认的赋值运算符用于处理同类对象之间的赋值,注意区分赋值与初始化
- 赋值: 修改已有对象的值
- 初始化: 创建新的对象
Person p;
Person p1 = p; //初始化
Person p2;
p2 = p; //赋值
编译器提供的默认赋值是成员赋值,如果成员是类对象,那么默认成员赋值将调用相应的赋值运算符。
如果需要显式定义复制构造函数,则基于相同的原因,需要显式定义赋值运算符
原型如下:
classname & cn::operator=(const classname & cn);
编译器不会生成将一种类型赋值给另一种类型的赋值运算符,如果需要
显式定义函数,重载=运算符
二、常见的类方法和操作
1、通常情况下,编写使用对象作为参数的函数时,应该按引用而不是按值来传递对象。这样做的原因是提高效率。按值传递涉及到生成参数的临时拷贝,即调用复制构造函数,然后调用析构函数删除临时拷贝。如果函数不修改对象,应将参数声明为const引用
2、 另一个原因是,当继承的基类有虚函数时,派生类的函数如果参数是基类对象的引用,那么在定义函数时可以传递派生类的对象
3、返回对象和返回引用
classname test(classname & cn); //返回对象
classname & test(classname & cn); //返回对象的引用
注意:如果函数 返回在函数中创建的临时对象,则不要返回引用
三、公有继承
使用公有继承要考虑的因素:
1、什么不能被继承?
构造函数,析构函数,赋值运算符,友元函数
2、如果派生类希望使用基类的友元函数,如何解决?
2.1 使用强制转换,将派生类指针或引用强制转换为基类指针或引用,然后使用转换后的指针或yy调用
2.2 使用运算符 dynamic_cast<>来进行强制类型转换
3、析构函数,通常情况下应该是虚的
4、对于派生类而言,保护的成员类似公有成员,对于外部而言,保护成员类似于私有成员。
派生类可以直接访问基类的保护成员