天天看点

二义性和C++消除二义性

1.二义性

       二义性的定义是:“如果文法G中的某个句子存在不只一棵语法树,则称该句子是二义性的。如果文法含有二义性的句子,则称该文法是二义性的。”(该定义来自于百度百科)用通俗的话讲,如果一句话或者一个符号具有两种以上的解释和含义,就说明该话或者符号具有二义性。二义性意味着含义不清和不明确。

       在计算机语言中,如果语言具有二义性,必须消除二义性,才能使程序正常运行。 

2.C++消除二义性

       C++中消除二义性的过程主要包括:1)重载消除二义性;2)和C语言混编消除二义性;3)多继承消除二义性。

1)重载消除二义性

       重载是C++等面向对象语言区别于C语言等纯面向过程语言的主要特性之一(随不是面向对象语言的三大特性之一)。泛型编程依赖于重载;作为静态多态的特性也依赖于重载。

       重载之所以能成功,是因为C++通过C++编译器,把同名函数转为不同的函数。也就是说,重载的同名函数,最终还是不同的函数。

       C++编译器在编译的时候,编译的规则如下:把同名函数,如void test() 和  void test(int),分别改为test()或_test 和 test(int) 或_test_int。       

void test(); //在编译器中改为test() 或 _test
void test(int); //在编译器中改为test(int) 或 _test_int
           

       这种改名方法,让C++可以重载同名函数。

       顺便说一下重载的意义:

       1)C++是一种强类型的语言,C++要保证类型的强制性;

       2)C++是一种面向对象语言,面向对象语言要使语言和抽象设计更加符合,和人类实际语言更加贴切;

       3)基于上面两个原则,重载才会存在。

2)C++和C混编消除二义性

        C++全部兼容了C语言的所有特性,C++是C语言的超集。但C++和C语言混编的时候,会出现各种问题。因为C语言本身不支持重载和多态,C语言的函数不存在改名或者始终是一个函数。这就会造成一个问题,如果C程序和C++混编,存在二义性问题。

       严格来说,C和C++混编的二义性,不是C程序造成的,而是C++对原来的纯C函数进行了改名。改名以后,原来的C程序找不到原来的函数,就会出现问题。

       为了避免该类问题,C++和C程序混编的时候,采用的方法是:C程序编译的时候,采用C语言的规则,并让C++编译器知道这部分程序是C语言的,并用C方法对C程序进行编译。

       使用方法为:使用extern "C"方法,让程序明白采用了C函数。通用规则如下;

#ifdef __cplusplus
extern "C" {
#endif

.......



#ifdef __cplusplus
}
#endif
           

3)多继承消除二义性

       C++支持多重继承,多重继承带来了便利,也带来了各种麻烦。(与愚蠢的C++(自嘲语,毕竟我靠C++吃饭这么长时间)相比,JAVA就避免了多重继承这个特性,并避免了这个麻烦)

       C++的多重继承问题,最主要体现为菱形继承。即基类被多个衍生类继承,多个衍生类被同一个子类继承。这会继承的子类最后出问题,即某个实体的含义不清楚。

       菱形继承图示如下:

二义性和C++消除二义性

      这样的多重继承,在最终类D里,会无法确定成员的含义。

      为了让大家理解多重继承的二义性,举个二义性的样例:

class Grand
{
public:
    Grand();
    ~Grand();

    void fun();
};

class FatherA: public Grand
{
public:
    FatherA();
    ~FatherA();
};

class FatherB: public Grand
{
public:
    FatherB();
    ~FatherB();
};

class Son: public FatherA, public FatherB
{
public:
    Son();
    ~Son();
};

int main(int argc, char *argv[])
{
    Son s;
    s.fun(); //二义性,因为不能确切属于哪个成员

    return 0;
}
           

       为了消除该二义性,方法如下:

       1)多重继承采用虚继承;

       2)采用 类名称::函数,即作用域加功能方法。

4.推广

       二义性问题其实在实践中广泛存在,并不仅仅属于C++和编程。这个问题属于任何领域。在工程中,消除任何意义的二义性,显然是一个工程师都应该做的。