上图可见: 调试结果, 调用了第三个重载函数, 并且c ( 9.8 ) 值强转为 int类型 ( 8 ) 相同方式测试 char 等类型也如此 函数重载二义性 问题来了!! 错误 以上这种情况就叫做二义性 解决二义性: 加入新的重载函数, 使用double类型形参 明确调用时实参强转类型: 传参前把数值强转为想要的类型 注: 编译器总是会把基本数据大的转为小的
在编程逻辑世界,有因必有果,如果一个结果含糊不定(二义性),显然是我们不想要的。C++11中引入nullptr是为了解决NULL的二义性问题。 NULL二义性的体现 void func(int) {} void func(int *) {} 当函数调用func(NULL)时会是怎样执行?
答案只有一个:消除函数的二义性。 函数的二义性 那什么是函数的二义性呢? ){} 这个时候就会出现歧义,因为这个函数有两种调用方式: function user(){}; // 普通方式调用 user(); // 当做构造函数调用 new user(); 这就是函数的二义性 因为函数的二义性,导致 JS 函数的复杂度直线上升,因为函数在创建的时候,创建者不知道未来的调用者如何调用,可能直接调用,也有可能通过 new 方法调用,这就会存在很大的安全隐患。 new.target){ throw('Uncaught TypeError: User is not a constructor') } } 所以调用者压根就不清楚函数的调用方式,这个函数的二义性 官方一直都知道这个问题,只是一直没解决,后来ECMAScript 6在给 JS 打补丁的时候,引入了两个概念: 箭头函数 class实例 它们的作用都是为了消除函数的二义性。
另外在 C++11 中也可以使用 Uniform initialization(统一初始化)来处理这种歧义: String ss{string(t)}; // ok 3.总结 这是一个经典的二义性问题
一、虚继承原理 1、虚继承解决继承二义性问题 继承的二义性 : 如果 一个 子类 ( 派生类 ) 继承多个 父类 ( 基类 ) , 这些父类 都继承了 相同的父类 , 那么 子类 访问 父类的父类 中的成员 , 就会产生 二义性 ; 报错 : error C2385: 对“x”的访问不明确 ; 使用 " 虚继承 " 可以解决上述问题 , 子类 继承父类时 , 在 访问限定符 之前使用 virtual 关键字 即可将 普通继承 改为 虚继承 ; 下面的代码中 A 是父类 ; B 类 和 C 类 虚继承 A 类 , 这样当 某个类 同时 多继承 B 类 和 C 类时 , 访问 A 类中的成员时 , 不会出现 二义性 ; 由于 B 和 C 虚继承 A , D 类访问 A 中的成员 , 不会产生二义性 ; class A { public: int x; }; // 子类 B 继承了父类 A 的 x 成员 class 二义性产生的原因 : 如果 上述 B 和 C 类 没有 虚继承 A 类 ; 上述 D 对象 创建时 , 会调用 两次 A 的构造函数 , 一次由 B 对象调用 , 一次由 C 对象调用 ; 此时 D
一、继承的二义性 1、场景说明 - 继承的二义性 A 类 是 父类 , B 类 和 C 类 继承 A 类 , 是 子类 , D 类 多继承 B 类 和 C 类 , 是 孙子类 ; 假如 A 类中有 成员变量 成员变量 x , D 类 多继承 B 类 和 C 类 , 会 分别从 B 和 C 各自 继承一个 成员变量 x ; D 类中 , 从 B , C 两个父类中继承自 爷爷类 A 的成员变量 , 会出现二义性 如果强行使用 对象.x 访问继承自 A , 会报错 error C2385: 对“x”的访问不明确 ; 定义 D 类的对象 d , 如果访问 继承自 A 类的 x 成员 , 则会出现二义性 ; // 定义 D 类对象 d D d; // 访问 继承自 A 类的 x 成员出现二义性 // 报错 error C2385: 对“x”的访问不明确 d.x = 40; 完整报错信息 : 1>--- ; 为了应对上述 继承的二义性 问题 , C++ 语言 使用 " 虚继承 " 解决 继承中的 二义性问题 ; C++ 中的 " 虚继承 " 是一种解决 多继承 带来的 菱形问题(diamond problem
_name = "peter"; // 需要显示指定访问哪个基类的成员可以解决二义性问题,但是数据冗余问题无法解决 a.Student::_name = "xxx"; a.Teacher::_ name = "yyy"; return 0; } 通过上面的运行结果我们发现,菱形继承存在两个问题:(1)二义性问题;(2)数据冗余问题。 为什么会产生二义性问题? 解决二义性的办法:指定类域(是Teacher类的Person还是Student类的Person) 数据冗余问题很好理解,就是Assistant中包含了两个Person类的信息,正常情况下应该只包含一份 1.3虚继承 有了多继承就可能有菱形继承,而菱形继承又存在二义性和数据冗余等问题。所以C++就引入了虚继承来解决数据的冗余问题。
有成员函数 和 成员变量 的 类 , 是不推荐的做法 , 实际开发中 , 绝对禁止 使用 上述类型的 多继承 ; 2、多继承弊端 多继承会带来一系列的问题 , 诸如 : 钻石问题 - 菱形继承结构 / 二义性错误 : 当一个类继承自多个类时 , 如果这些类有共同的基类 , 那么会出现菱形继承结构 , 也称为钻石问题 ; 该场景下 , C++ 编译器 无法确定应该使用哪个基类的成员 , 产生 二义性 ; 成员变量名相同 - 二义性错误 : 子类继承多个父类 , 父类之间没有相同的父类 , 但是 父类中 有相同名称的成员变量 , 此时也会产生二义性问题 , 需要使用域作用符访问父类中相同名称的成员 ; 破坏封装性 : ; 在 菱形继承结构 中 , 虚继承可以使 重复继承 的 父类 , 只继承 依次 ; 多继承的二义性 参考 【C++】继承 ⑫ ( 继承的二义性 | virtual 虚继承 ) 博客 ; 二、代码示例 ========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ========== 2、代码示例 - 使用虚继承解决菱形继承结构的二义性 在下面的 菱形继承结构 中 , D
2.9 文法的二义性若一个文法存在某个句子或句型,它存在两棵不同的语法树,则称该句子或句型是二义性的,对应的文法也是二义性的。 二义性不可判定,从底向上看,二义性意味着句柄不唯一,解决二义性的方法是,加以限制,人为避免产生二义性2.9.1 有关文法的实用限制多余规则:指文法中任何句子的推导都不会用到的规则,若有则删去- 不可到达
问题2:二义性 二义性是指在菱形继承的情况下,派生类可能会有两个或更多的基类提供了相同的函数或数据成员,这在调用时会导致编译器无法确定调用哪个版本。 例如,如果基类A和B都有一个同名的函数,而在派生类中没有明确指定调用哪一个,就会产生二义性错误。 6、多继承时的虚继承:当多个类同时virtually继承同一个虚基类时,虚基类的成员变量和成员函数在子类中只会存在一份,避免了冗余性和二义性问题。 此时,BaseClass 的成员变量 var 在 FinalChild 中只有一份,并且不会发生二义性问题。 总之,C++ 通过虚继承解决了菱形继承中的冗余性和二义性问题,使得在使用继承时更加灵活和安全。
3.1 什么是二义性问题?所谓的二义性问题指的是代码或表达式存在多种理解或解释,导致程序的含义不明确或模糊。 3.1 可证伪的HashMapHashMap 之所以不怕二义性问题的原因是,HashMap 的设计是给单线程使用的,而单线程下的二义性问题是能被证明真伪的,所以也就不存在二义性问题了(能被证明的问题就不是二义性问题 这样二义性问题就得到了解决,所以 HashMap 的二义性问题可被证明真伪,所以就不怕二义性问题,因此也就可以给 key 或者 value 设置 null 了。 ,所以二义性问题是真实存在的。 因为在你在证明二义性问题的同时,可能会有另一个线程影响你的执行结果,所以它的二义性问题就一直存在。
什么是文法的二义性? 1)分成四种类型,即0型、1型、2型和3型。 2)如果文法G中的某个句子存在不只一棵语法树,则称该句子是二义性的。如果文法含有二义性的句子,则称该文法是二义性的 2. 什么是文法的二义性?给出一个二义性文法实例 (1)如果文法G中的某个句子存在不只一棵语法树,则称该句子是二义性的。 如果文法含有二义性的句子,则称该文法是二义性的 书上:若一个文法中存在某个句子,有两个不同的最左(最右)推导,则该文法是二义的。 局部优化、循环优化、全局优化 7.用实例说明简单栈式存储分配的过程: 对于没有分程序结构,过程定义不允许嵌套但允许过程递归调用的语言,我们可以采用一:种简单的栈式存储分配策略。
但随之而来的挑战是:多个基类的作用域重叠可能导致名字冲突(二义性,Ambiguity),例如两个基类拥有同名的成员变量或函数。 二、多重继承二义性的典型类型与代码示例 2.1 成员变量的二义性:同名变量冲突 当多个基类定义了同名的成员变量时,派生类对象访问该变量会引发二义性。 若在多重继承的多个基类作用域中找到同名的声明(无论这些声明是否等价),则视为二义性,编译器拒绝编译。 5.2 二义性对赋值的影响 若多个基类存在同名成员,且未显式覆盖,直接赋值会引发二义性。 二义性类型 成员变量、成员函数、虚函数、菱形继承的公共基类均可能引发二义性。 二义性解决方案 显式作用域限定、派生类重写成员、虚继承、using 声明。
具体解释 因为汉字处理系统要保证中西文的兼容,当系统中同时存在ASCII码和汉字国标码时,将会产生二义性。 机内码:为了避免ASCII码和国标码同时使用时产生二义性问题,大部分汉字系统都采用将国标码每个字节高位置1作为汉字机内码。 这样既解决了汉字机内码与西文机内码之间的二义性,又使汉字机内码与国标码具有极简单的对应关系。 “汉字机内码获取工具”百度云下载链接: 链接:https://pan.baidu.com/s/1y0nYgMYSqv7UmKD7EMLJkg 提取码:quwu
菱形继承带来的问题 菱形继承可以带来一些问题,主要是关于数据冗余和二义性。 类和Teacher类都有相同的成员,那么当在Assistant类中调用这个成员时,会出现二义性。 例如: void Test() { // 这样会有二义性无法明确知道访问的是哪一个 Assistant a; a. 7.笔试面试题 什么是菱形继承?菱形继承的问题是什么? ①菱形继承是多继承的一种特殊情况。 ②数据冗余和二义性。 什么是菱形虚拟继承?如何解决数据冗余和二义性的? ①为了解决数据冗余和二义性问题,C++提供了虚继承的机制。
图4-7 继承的访问能力(续) 4.2.6 基类与派生类的关系 1派生类是基类的具体化基类是对若干个派生类的抽象,而派生类是基类的具体化;基类抽取了它的派生类的公共特征,而派生类通过增加行为将抽象类变为某种有用的类型 图4-12 多继承构造函数调用顺序 4.3 二义性问题 原因:在多继承情况下,造成的对基类中某个成员的访问出现的不唯一的情况 4.3.1 成员函数二义性 ? ,则对该基类中说明的成员进行访问时,可能会出现二义性 4.3.2 成员变量二义性 ? 图4-14 成员变量二义性 解决办法:1区别出是通过类B1或类B2调用类A的a,c1.B1::a或c1.B2::a 2虚基类 4.3.3 解决办法 利用成员名限定法消除二义性 在类中定义一个同名成员 虚基类 4.3.4 特殊说明 一个类不能从同一个类中直接继承一次以上 二义性检查在访问控制和类型检查之前进行,访问控制和类型检查不能解决二义性问题 4.3.5 示例 ?
虚继承 为了解决菱形继承的二义性和数据冗余的问题,提出了虚继承 探讨一下虚继承是如何解决数据冗余和二义性的 不是虚继承版本 ---- #include<iostream> using namespace = 5; return 0; } 发现在监视下存在三份数据,而内存中只存在一份数据 ---- 发现相比于不是虚继承的版本,03 和04 上面多个一行地址存在 ---- 使用 地址48 7b 32 00 加上偏移量20 正好为 02 00 00 00 即A的地址 使用 地址54 7b 32 00 加上偏移量12 正好为 02 00 00 00 即A的地址 因为B类和C类中都存在A, 存在数据冗余和二义性,所以把A的数据放入公共区域,既不放入B中,也不放入C中 通过偏移量来寻找这个公共的位置 在这两个表中,偏移量没有存到第一个位置,因为第一个位置是为以后的多态做准备的 ---- 使用虚继承是为了解决数据冗余的问题 感觉变大是因为A太小了,解决数据冗余和二义性,会增加了两个指针,只节省了一个A,即4个字节,相当于多消耗了4个字节 如果A变大,就不亏了,指向的空间忽略不计,会创建很多对象,每个对象都指向该空间,由大家共同分担
这导致在函数重载时可能会出现二义性问题。 2. NULL 在 C 和 C++ 中的区别 在 C 语言中,NULL 定义为 (void *)0,它可以隐式转换为任何类型的指针。 C++11 引入 nullptr 的原因 为了消除 NULL 在 C++ 中的二义性问题,C++11 引入了 nullptr 作为一种新型空指针常量。 ptr)" << endl; } int main() { f(nullptr); // 调用 f(int* ptr) return 0; } nullptr 的引入解决了函数重载中的二义性问题 相比之下,nullptr 是一种更加安全且明确的选择: 在C++中NULL 通常是整数 0,会引发二义性问题。 nullptr 是一个指针常量,可以隐式转换为任意指针类型,不会引发二义性问题。 T> operator T C::*() const { return 0; } private: void operator&() const; } nullptr = {}; 7.
7. 复杂的菱形继承及虚拟继承 7.1 菱形继承问题 菱形继承是 C++ 多重继承中的一种特殊情况。当一个类从两个基类继承,而这两个基类又有共同的基类时,就会形成一个菱形结构。 这就导致了数据冗余和访问的二义性。 7.2 菱形继承的二义性问题 在上述代码中,假如B类中存在与A类相同的成员变量名,使用D构造出一个对象,当D访问该变量名时,无法确定访问哪一个。 _a = 7; // true:通过虚基表解决了二义性 return 0; } D对象通过虚基表定位到A的唯一实例,访问A类的成员变量 _a 合法。 消除二义性:解决了菱形继承中的二义性问题,使得派生类可以明确继承父类。 控制构造函数:通过虚拟继承,派生类避免了多次调用同一基类的构造函数。 8.5 二义性问题 传统继承: 传统继承会产生二义性问题,特别是当多重继承导致多个相同的基类副本时。
一开始用的CentOS7安装的tomcat7,CentOS7自带了httpd服务,80端口是被占用的,卸载了httpd服务后,安装好了openjdk之后安装tomcat7,接着发现默认的端口是8080, 用了netstat命令查看一下端口占用情况发现CentOS7居然没有这个命令,这不科学啊,具体的原因没去分析,更坑爹的是service tomcat iptables命令改成了systemctl start 好无语,在CentOS7上死活没折腾出结果,改成1024以上的端口都是好使的,低于1024的端口都不行,我估计是权限的问题,默认1024下的端口不给权限应该。 我直接运行命令 apt-get update apt-get install java-package apt-get install tomcat7 一切完事之后就是修改端口号, /etc/tomcat7 接着重启服务 service tomcat7 start 好了,ok。