*今天继续昨天的C++类来写,首先还是初始化列表的还有几个应用。
C++类1 https://www.godreams.cn/?post=78
可以先看一下1再来看2
②初始化列表在类继承时的作用
在使用类的继承时,如果父类构造函数中有参数,且没有提供无参的构造函数的情况下,那么,在子类进行初始化时(构造函数时)就需要对父类进行带参的初始化,这时,初始化列表就派上用场了,下面我们看一个实例:
#include<iostream> using namespace std; class Animal { public: Animal(int weight,int height): //没有提供无参的构造函数 m_weight(weight), m_height(height) { } private: int m_weight; int m_height; }; class Dog: public Animal { public: Dog(int weight,int height,int type) //error 构造函数 父类Animal无合适构造函数 { } private: int m_type; }; int main(){ Dog a(1,2,3); }在这个例子中我们使用了常用的动物父类,为其创造一个子类dog。但在父类构造函数中存在有参数的接收,子类dog构造时并未向其传递参数,因此会显示编译出错。我们只需要在第20行使用初始化列表向父类传递参数便可以了:
Dog(int weight,int height,int type):Anima(weight,height)初始列表部分参考文章https://blog.csdn.net/chenlycly/article/details/53558675
总得来说使用初始化列表实现对象的初始化有很多优点,也更加高效,因此,推荐使用初始化列表而不是在函数内进行初始化。
复制构造函数(拷贝构造函数)
复制构造函数,不能理解为 复制“构造”函数 而应该理解为 “复制”这一操作的构造函数。感觉这样子就很好理解了。下面我们来看一个实例:
#include<iostream > using namespace std; class Complex { public: double real, imag; Complex(double r, double i) { real= r; imag = i; } }; int main(){ Complex cl(1, 2); Complex c2 (cl); //用复制构造函数初始化c2 cout<<c2.real<<","<<c2.imag; //输出 1,2 return 0; }在这个实例中,我们并没有写复制构造函数,所以编译器使用了默认的构造函数,如果我们写了构造函数的话,就会覆盖其默认构造函数:
#include<iostream> using namespace std; class Complex{ public: double real, imag; Complex(double r,double i){ real = r; imag = i; } Complex(const Complex & c){ real = c.real; imag = c.imag; cout<<"Copy Constructor called"<<endl ; } }; int main(){ Complex cl(1, 2); Complex c2 (cl); //调用复制构造函数 cout<<c2.real<<","<<c2.imag; return 0; }第9行便是我们的复制构造函数了,注意我们传入的是const类型参数,以此来防止函数对其造成修改,当然,我们也可以不加,只是这样更加安全罢了。
在自定义的复制构造函数中,我们并不一定要实现“复制”,而可以自定义其“复制”规则。
传参复制中的拷贝构造函数
复制构造函数会发生传参复制过程中,我们下面看这段代码来进行理解:
#include<iostream> using namespace std; class A{ public: A(){}; A(A & a){ cout<<"Copy constructor called"<<endl; } }; void Func(A a){ } int main(){ A a; Func(a); return 0; }在这个实例中,我们看到在Func中并没有输出什么,但程序执行之后会显示Copy constructor called 这是为什么呢?原来在将对象a传递给函数Func时,调用了复制构造函数。这个我感觉可以理解为,之前不是讲的函数调用的时候是引用了一个复制后的对象,所以在给Func传a的时候有了输出。
返回对象中的复制构造函数
当一个函数返回的类型是一个对象时,也会默认调用复制构造函数,我们看一个实例:
#include<iostream> using namespace std; class A { public: int v; A()=default; A( A & a) { v = a.v; cout << "Copy constructor called" << endl; } }; A Func() { A a; return a; } int main() { Func(); return 0; }
这个实例中,函数Func直接定义一个对象a,并将其作为返回值,在主函数调用时,经过程序运行我们可以发现,其输出语句正是我们在复制构造函数中的输出,这一过程便是发生在return这一过程中的。
复制构造函数这部分参考自http://c.biancheng.net/view/151.html
原文部分函数并不符合其条件,我在写的时候将其修改了。
如果大家想要扩展或者说深入理解复制(拷贝)这一概念,推荐一下这篇文章,关于深拷贝和浅拷贝的理解https://blog.csdn.net/weixin_41143631/article/details/81486817
类的组合
类的组合相对于之前的概念就要好理解的多了,类的组合,顾名思义就是将类组合起来使用,我们来看下面这个实例:
#include<iostream> using namespace std; class Point{ public: Point(){ }; Point(Point &p); }; Point::Point(Point &p) { cout << "Point拷贝构造函数被调用"<< endl; } // 组合类的构造函数 class Distance{ public: Point p1,p2; Distance(Point a, Point b); }; Distance::Distance(Point a, Point b):p1(a),p2(b) { cout << "Distance构造函数被调用" << endl; } int main() { Point p3,p4; Distance my(p3, p4); }这一部分与拷贝构造函数相结合比较合适,同时也运用到了组合类,Distance类中使用了Point类,其实很多时候我们都是不经意间就使用了组合类,其应用十分广泛。上述代码运行结果如下图:
我们看到Point的拷贝构造函数被调用了四次,这是因为在代码第27行首先调用两次,在20行的初始化列表处又调用了两次。(感觉这个应该在拷贝构造函数里说)
前向引用声明
有时候会遇到两个类同时互相使用的情况,这时候就要使用前向引用声明了。
class B; //前向引用声明 class A { public: void f(B b); }; class B { public: void g(A a); };因为在A使用B的时候B并没有被创建,其实际执行过程中,也会先执行B类的代码,之后再执行A类。
组合类这部分来自http://blog.sina.com.cn/s/blog_a0c22cfd0102xaky.html
但原文代码不完善,可以综合来看吧。
结语
好了,至此关于郑莉老师第四章和类相关的难点大致写完了,一定还有许多难点,等着继续学习吧。