会飞的鱼

2020
Godam
首页 » 学习笔记 » C++ 类 2

C++ 类 2

*今天继续昨天的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

    但原文代码不完善,可以综合来看吧。

结语

    好了,至此关于郑莉老师第四章和类相关的难点大致写完了,一定还有许多难点,等着继续学习吧。

文章如无特别注明均为原创! 作者: 果果, 转载或复制请以 超链接形式 并注明出处 GODAM|博客|godam
原文地址《 C++ 类 2》发布于2020-3-11

分享到:
打赏

评论

游客

切换注册

登录

您也可以使用第三方帐号快捷登录

切换登录

注册

sitemap