c++学习笔记 -- 继承(2)

声明:代码出处:http://www.weixueyuan.net/view/6359.html

从基类派生出派生类,派生类继承基类的继承方式有三种:public、protected和private。在未指定的情况下编译器会默认继承方式为protected或private方式。

1) public继承方式
  • 基类中所有public成员在派生类中为public属性;
  • 基类中所有protected成员在派生类中为protected属性;
  • 基类中所有private成员在派生类中不可访问。

2) protected继承方式
  • 基类中的所有public成员在派生类中为protected属性;
  • 基类中的所有protected成员在派生类中为protected属性;
  • 基类中的所有private成员在派生类中仍然不可访问。

enum language{cpp, java, python,javascript, php, ruby};

class book
{
public:
    void setprice(double a);
    double getprice()const;
    void settitle(char* a);
    char * gettitle()const;
    void display();
private:
    double price;
    char * title;
};

class codingbook: protected book
{
public :
    void setlang(language lang);
    language getlang(){return lang;}
private:
    language lang;
};

3) private继承方式
  • 基类中的所有public成员在派生类中均为private属性;
  • 基类中的所有protected成员在派生类中均为private属性;
  • 基类中的所有private成员在派生类中均不可访问。
enum language{cpp, java, python,javascript, php, ruby};

class book
{
public:
    void setprice(double a);
    double getprice()const;
    void settitle(char* a);
    char * gettitle()const;
    void display();
private:
    double price;
    char * title;
};

class codingbook: private book
{
public :
    void setlang(language lang);
    language getlang(){return lang;}
private:
    language lang;
};

使用using声明可以改变基类成员在派生类中的访问属性。我们知道基类的公有成员经过公有继承,在派生类中其属性为public的,但是通过using 声明,我们可以将其改为private或protected属性。

enum language{cpp, java, python,javascript, php, ruby};


class book
{
public:
    void setprice(double a);
    double getprice()const;
    void settitle(char* a);
    char * gettitle()const;
    void display();
private:
    double price;
    char * title;
};


class codingbook: public book
{
public :
    void setlang(language lang);
    language getlang(){return lang;}
private:
    language lang;
        using book::setprice;
};


下面的主函数就会编译错误,在think类对象调用setlang和settitle函数时都不会有问题,因为这两个函数的属性为public,可以访问。唯独setprice函数通过using声明后,由public属性变为了private属性了。

int main()
{
    codingbook think;
    think.setlang(cpp);
    think.settitle("Thinking in C++");
    think.setprice(78.9);  //compile error
    return 0;
}

派生类中新增一个成员变量,该成员变量与基类中的成员变量同名,则新增的成员变量就会遮蔽从基类中继承过来的成员变量。同理,如果派生类中新增的成员函数与基类中的成员函数同名,则该新增的成员函数就会遮蔽从基类中继承过来的成员函数。


#include<iostream>
using namespace std;

class basic
{
public:
    void setx(int a){x = a;}
    void sety(int b){y = b;}
    int getx(){return x;}
    int gety(){return y;}
private:
    int x;
    int y;
};

class derived : public basic
{
public:
    void setx(char *a){x = a;}
    char* getx(){return x;}
private:
    char * x;
};

int main()
{
    derived d1;
    d1.setx("class");   //OK
    d1.setx(50);        //compile error
    d1.basic::setx(50); //OK
    return 0;
}


本例中定义了一个基类basic,之后通过继承basic类派生出derived类。需要注意的是在basic类中定义了一个成员变量x,该变量是int型,与之对应的成员函数是setx和getx函数。而派生类中同样定义了一个成员变量x,而它是char指针类型,与之对应的成员函数是setx和getx函数。在主函数中,定义了derived类的对象d1,我们在调用setx(char *)函数时,没有问题。接着又调用从基类中继承过来的setx(int)函数,结果编译出错。最后通过类名来调用基类继承过来的setx(int)函数,编译通过。在派生类derived中,setx(char *)与基类继承过来的setx(int)函数同名,如此一来,派生类新增的函数setx(char *)遮蔽了从基类继承而来的setx(int)函数,如此一来通过d1.setx(50)调用setx(int)是不成功的,故而出现编译错误,正确的调用方法是通过类名来调用:d1.basic::setx(int)。


从上例中,我们可以看出被遮蔽了的基类的成员变量或成员函数并非是没有继承过来,而仅仅是被派生类的同名成员变量和成员函数给遮蔽了,调用的时候需要用到类名加上域解析操作符。


C继承自类B,类B继承自类A。那么类C中的除了能够继承B类的成员函数和成员变量外,同样也能继承B类继承自A类的所有成员。换言之,类C可以继承来自类A的所有成员。因此继承既可以是直接继承,也可以是间接继承。


class A
{
public:
    int getx(){return x;}
    void setx(int a){x = a;}
private:
    int x;
};

class B : public A
{
public:
    int gety(){return y;}
    void sety(int b){y = b;}
private:
    int y;
};

class C : public B
{
public:
    int getz(){return z;}
    void setz(int c){z = c;}
private:
    int z;
};


先定义了类A,然后通过派生定义了类B,之后再派生出类C。B类和C类都有自己新增的成员变量和成员函数。下面我们将以表格的形式列出类A、B和C的所有成员和成员变量。


,派生类同样有构造函数。当我们创建一个派生类对象的时候,基类构造函数将会被自动调用,用于初始化派生类从基类中继承过来的成员变量。而派生类中新增的成员变量则需要重新定义构造函数用于初始化了。


#include<iostream>
using namespace std;

class book
{
public:
    book();
    book(char* a, double p = 5.0);
    void setprice(double a);
    double getprice()const;
    void settitle(char* a);
    char * gettitle()const;
    void display();
private:
    double price;
    char * title;
};

class book_derived :public book
{
public:
    void display();
};

book::book(char* a, double p) 
{
    title = a;
    price = p;
}

book::book()
{
    title = "NoTitle";
    price = 0.0;
}

void book::setprice(double a)
{
    price = a;
}

double book::getprice()const
{
    return price;
}

void book::settitle(char* a)
{
    title = a;
}

char * book::gettitle()const
{
    return title;
}

void book::display()
{
    cout<<"The price of "<<title<<" is $"<<price<<endl;
}

void book_derived::display()
{
    cout<<"The price of "<<gettitle()<<" is $"<<getprice()<<endl;
}

int main()
{
    book_derived b;
    b.display();
    return 0;
}


book_derived类,该类没有自身的成员变量,类中所有成员变量都继承自book类,类中成员函数仅有一个display函数,该函数遮蔽了基类book中的display函数。在主函数中定义派生类的对象b,之后调用派生类的display函数,程序运行结果为:“The price of NoTitle is $0”。

从这例1中,我们不难看出派生类在创建对象时会自动调用基类构造函数。如果像例1这种情况,派生类中没有新增成员变量,基类的构造函数功能已经满足派生类创建对象初始化需要,则派生类则无需重新自定义一个构造函数,直接调用基类构造函数即可。如果派生类中新增了成员变量,这时如果需要在创建对象时就进行初始化则需要自己设计一个构造函数


#include<iostream>
using namespace std;

enum language{none, cpp, java, python, javascript, php, ruby};

class book
{
public:
    book();
    book(char* a, double p = 5.0);
    void setprice(double a);
    double getprice()const;
    void settitle(char* a);
    char * gettitle()const;
    void display();
private:
    double price;
    char * title;
};

class codingbook: public book
{
public :
    codingbook():book(){lang = none;}
    codingbook(language lang, char * t, double p);
    void setlang(language lang);
    language getlang(){return lang;}
    void display();
private:
    language lang;
};

book::book(char* a, double p) 
{
    title = a;
    price = p;
}

book::book()
{
    title = "NoTitle";
    price = 0.0;
}

void book::setprice(double a)
{
    price = a;
}

double book::getprice()const
{
    return price;
}

void book::settitle(char* a)
{
    title = a;
}

char * book::gettitle()const
{
    return title;
}

void book::display()
{
    cout<<"The price of "<<title<<" is $"<<price<<endl;
}

void codingbook::setlang(language lang)
{
    this->lang = lang;
}

codingbook::codingbook(language lang, char * t, double p):book(t,p)
{
    this->lang = lang;
}

void codingbook::display()
{
    book::display();
    cout<<"The language is "<<lang<<endl;   
}

int main()
{
    codingbook cpp;
    cpp.display();
    codingbook java(java, "Thinking in Java", 59.9);
    java.display();
    return 0;
}

定义了两个类book类和codingbook类,codingbook类是book类的派生类。在codingbook类中新增了一个language成员变量,为此必须重新设计新的构造函数。在本例中book类中有一个默认构造函数和一个带参数的构造函数,codingbook类中同样声明了两个构造函数,一个默认构造函数和一个带参数的构造函数,默认构造函数显式调用基类的默认构造函数,带参构造函数显式调用基类的带参构造函数。在主函数中定义了codingbook类的对象cpp,该对象调用codingbook类的默认构造函数,codingbook类中的默认构造函数先会调用基类的默认构造函数将title和price进行初始化,之后才会执行自身函数体中的内容。之后又定义了codingbook类对象java,该对象在定义时后面接有三个参数,很明显是需要调用codingbook类的带参构造函数,其中java参数用于初始化lang成员变量,而后两个参数则用于初始化从基类继承过来的title和price两个成员变量,当然初始化顺序依然是先调用基类的带参构造函数初始化title和price,然后再执行自身函数体中的初始化代码初始化lang成员变量


#include<iostream>
using namespace std;

class base
{
public:
    base(){x = 0; y = 0; cout<<"base default constructor"<<endl;}
    base(int a, int b){x = a; y = b; cout<<"base constructor"<<endl;}
private:
    int x;
    int y;
};

class derived: public base
{
public:
    derived():base(){z = 0; cout<<"derived default constructor"<<endl;}
    derived(int a, int b, int c):base(a,b){z = c; cout<<"derived constructor"<<endl;}
private:
    int z;
};

int main()
{
    derived A;
    derived B(1,2,3);
    return 0;
}


定义了两个类,基类base中定义了一个默认构造函数和一个带参数的构造函数。派生类derived中同样定义了两个构造函数,这两个构造函数一个为默认构造函数,一个为带参构造函数。派生类中的默认构造函数显式调用基类默认构造函数,带参构造函数显式调用基类的带参构造函数。我们在主函数中定义了派生类的两个对象,这两个对象一个是调用派生类的默认构造函数,另一个调用派生类的带参构造函数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值