Chinaunix首页 | 论坛 | 博客
  • 博客访问: 287820
  • 博文数量: 148
  • 博客积分: 4365
  • 博客等级: 上校
  • 技术积分: 1566
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-05 21:38
文章分类
文章存档

2014年(2)

2013年(45)

2012年(18)

2011年(1)

2009年(54)

2008年(28)

我的朋友

分类: C/C++

2008-10-29 20:28:40

当一个名字在派生类的作用域没有找到时,编译器会去其相应的基类的作用域中寻找。
指针与引用的静态类型决定了它们的行为(是指能调用哪些函数和访问哪些成员吧),当基类型的引用或指针实际指向派生类的对象时,通过该指针或引用也只能调用基类中的函数(虚函数例外)。

class base{
public:
    void func(){cout<<"in base\n";}

};

class derived:public base{
public:
    void func(){cout<<"in derived\n";}
 
};

int main(void) {
    derived obj_d;

    derived &ref_d=obj_d;
    base &ref_b=obj_d;
    ref_b.func();
    ref_d.func();

    cout<<"end in main\n";
    return EXIT_SUCCESS;
}

运行结果

in base
in derived
end in main

如果我们将基类中的func函数注释掉,则编译会报错
no matching function for call to `base::func()'

可以看到,通过派生类引用调用的是派生类中的函数,说明当派生类与基类有相同的名字时,派生类的会隐藏掉基类中的版本,函数与数据成员都是如此。即使函数原型不同也是如此。将上例中基类的func函数改为

void func(int i){cout<<"in derived\n";}

需要接收一个整形参数,主函数为

int main(void) {
    derived obj_d;
    obj_d.func(3);

    cout<<"end in main\n";
    return EXIT_SUCCESS;
}

看通过这样的方式能否调用到基类中的版本,但是程序编译会报错
no matching function for call to `derived::func(int)'
candidates are: void derived::func()
原因在于编译器在派生类中找到了func这个名字,就不会再向上查找了,即使这是个重载函数,调用时发现这不是派生类中的函数版本,所以报错。只有当派生类中没有该函数的定义时,编译器才会继续向上查找func这个名字,在基类中发现,并调用它。
就像局部域中的函数不会重载(overload)全局域中的函数,只会覆盖它(override).

对于基类中的重载函数,派生类要想继续使用它们,要么在其中全部对它们进行定义并按照自己的需求进行修改,要么一个也不定义。如果只定义了其中的一部分,那么派生类只拥有其定义的这一部分。

class base{
public:
    void func(){cout<<"in base\n";}
    void func(int i){cout<<"in base and i=<<"<<i<<"\n";}

};

class derived:public base{
public:
    void func(){cout<<"in derived\n";}
};

int main(void) {
    derived obj_d;
    obj_d.func(3);
    cout<<"end in main\n";
    return EXIT_SUCCESS;
}

编译报错
 no matching function for call to `derived::func(int)'
 candidates are: void derived::func()

这样当基类中重载函数过多时,工作会很麻烦,如果在派生类中想拥有全部的重载函数,可以使用using语句。

class derived:public base{
public:
    using base::func;
};

这样派生类就拥有了全部的重载函数,只需根据需要修改相应的版本即可。
注意:using 语句使用函数名字时,不可带参数

现在可以理解为什么当需要动态绑定时,派生类与基类中的虚函数必须函数原型相同,如果原型不同,通过基类的指针或引用调用时,只会调用到基类中的版本。

class base{
public:
    virtual void func(){cout<<"in base\n";}
};

class derived:public base{
public:
    void func(int i){cout<<"in derived\n";}
  };

int main(void) {
    derived obj_d;
    base &ref_b=obj_d;
    ref_b.func();

    cout<<"end in main\n";
    return EXIT_SUCCESS;
}

派生类修改了基类中的虚函数,加上了整形参数,这时派生类中的func函数已经把基类中版本隐藏了,同时派生类中的func函数已经不是虚函数了。

in base
end in main


c++中名字的查找过程为:
1.根据对象,指针,引用的静态版本决定哪一个函数被调用。
2.到相应的类中查找,如果没有找到,则到该类的直接基类中查找,按此过程下去都没找到,报错。
3.找到,做原型检查,看调用是否正确。
4.正确,判断是否动态绑定,调用相应版本。

纯虚函数与抽象类
如有一个汽车的类,而我们要操作的是轿车,卡车等其子类,同时又不希望产生出汽车类的对象以避免错误,怎么办?c++中的抽象类与纯虚函数应运而生。
当一个类我们只让其作为中间基类而使用它所派生出的其他类时,可以将其定义为抽象类
只需在该类中定义一个虚函数,并在其原型后加上"=0"

class base{
public:
    virtual void func()=0;

};

class derived:public base{
public:
    void func(){cout<<"in derived\n";}

};

int main(void) {
    base obj_b;

    cout<<"end in main\n";
    return EXIT_SUCCESS;
}

企图创建一个base的对象,编译器报错
In function `int main()':
cannot declare variable `obj_b' to be of type `base'
because the following virtual functions are abstract:
virtual void base::func()
但是可以创造其子类的对象

int main(void) {
    derived obj_d;
    cout<<"end in main\n";
    return EXIT_SUCCESS;
}

注意:在抽象类的子类中一定要有对抽象类中纯虚函数的重写,哪怕只有空的函数体,否则是创建不了抽象类的子类对象的!!!!!
阅读(771) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~