谈谈C++的缺陷:一辆公共汽车是一辆汽车,一堆公共汽车却不是一堆汽车,该怎么解决
谈谈C++的缺陷:一辆公共汽车是一辆汽车,一堆公共汽车却不是一堆汽车这是一个不合逻辑的东西,个人认为这是C
谈谈C++的缺陷:一辆公共汽车是一辆汽车,一堆公共汽车却不是一堆汽车 这是一个不合逻辑的东西,个人认为这是C++的缺陷之一,当然不同的人有不同的看法. C++并不是像我想像中的那么完美.
C/C++ code#include <vector>using std::vector;class base{};class derived : public base{};void foo( vector<base*> v ){}int main( int argc, char* argv ){ vector<derived*> vd; foo( vd );} 1>e:\my projects\stlex1\contypetest\main.cpp(16) : error C2664: 'foo' : cannot convert parameter 1 from 'std::vector<_Ty>' to 'std::vector<_Ty>'
1> with
1> [
1> _Ty=derived *
1> ]
1> and
1> [
1> _Ty=base *
1> ]
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
[解决办法] 不要把编译器想得这么智能
[解决办法] 这应该不是一堆汽车的问题,应该说是两个不同的装汽车的箱子。就像你用参数char a[4],你就不能用char a[5]传过去一样。
[解决办法] 这个帖子挺有意思的。
我觉得也可以这样来理解:
我们买了几团毛线,其中一部分织成毛衣,另一部分织成了毛裤。然而,我们却不能说毛线、毛衣、毛裤都是一样的东西。
vector是用来组合一组相同类型的数据的容器,组合和继承的差别就在于ISA关系不能继续。
如果这样:
C/C++ codeclass CarsList{ vector<Car *> m_cars;public: ... virtual BuyCar(){m_cars.push_back(new Car);}};class BusesList: public CarsList{public: virtual BuyCar(){m_cars.push_back(new Bus);}};[解决办法] 这和你用中文的语意描述有关. 这里修饰的是vector,元素不同的两个vector,两个不同的类.它们之间并无继承关系. 话说回来,本就没有完美的语言,C++也并不例外.[解决办法] C/C++ code一堆公共汽车应该是#include <vector>using std::vector;class base{};class derived : public base{};void foo(base* vb, size_t num){}int main( int argc, char* argv ){ derived* vd = new derived[10]; foo( vd, 10 );}当你使用vector<>的时候,它就不只只是一堆公共汽车了,因为它除了包含一堆公共汽车外,还包含了别的属性,比如大小,容量[解决办法] 现实中物件间的关系可不止一个简单的“是”关系。 想把所有关系都套到“是”关系上,到处碰壁也就不奇怪了。[解决办法] 我倒是觉得这个是属于容器类自己的问题。 C/C++ code#include <vector>using std::vector;class base{};class derived : public base{};//void foo( vector<base*> v )void foo( base v[] ){}int main( int argc, char* argv ){ derived vd[10]; foo( vd );}[解决办法] LZ理解错误。把容器去掉就没有问题,有了容器就不是那么回事了。容器本身还有自己的属性[解决办法] 探讨 C/C++ code一堆公共汽车应该是 #include<vector>usingstd::vector;classbase{};classderived :publicbase{};voidfoo(base*vb, size_t num) { }intmain(intargc,char*argv ) { derived*vd=newderived[10]; foo( vd,10); } 当你使用vector<>的时候,它就不只只是一堆公共汽车了,因为它除了包含一堆公共汽车外,还包含了别的属性,比如大小,容量[解决办法] 模板是用来实现静态多态的。就是相同的代码,在实例化后,变成了不同的东西。[解决办法] 探讨 我倒是觉得这个是属于容器类自己的问题。 C/C++ code #include <vector> using std::vector; class base{}; class derived : public base{}; //void foo( vector<base*> v ) void foo( base v[] ) { } int main( int argc, char* argv ) { derived vd[10]; foo( vd ); } 这样的话就没问题了,虽然数组在进入函数的时候会若化成指针,但是这个似乎也能看成是一堆公共汽车等于一堆汽车。
[解决办法] 探讨 引用: 我倒是觉得这个是属于容器类自己的问题。 C/C++ code #include <vector> using std::vector; class base{}; class derived : public base{}; //void foo( vector <base*> v ) void foo( base v[] ) { } int main( int argc, char* argv ) { derived vd[10]; foo( vd ); } 这样的话就没问题了,虽然数组在进入函数的时候会若化成指针,但是这…[解决办法] 引用楼主 yndfcd 的帖子: 这是一个不合逻辑的东西,个人认为这是C++的缺陷之一,当然不同的人有不同的看法. C++并不是像我想像中的那么完美. [解决办法] 既然用了指针作为VECTOR的元素,那么完全可以vector<base*> vd; 而不是vector<derived*> vd;[解决办法] 如果真要按vLZ说的那样 vector<base*>和vector<Drived*>应该也有继承关系[解决办法] vector是一个模板类,故,对于模板类,模板参数不一样,对应生成的类不一样! 比如:vector<A>和vector<B>是完全不同的两个类![解决办法] 1. c++ 本身就不是什么“完美的语言”,这个世界就没有完美的语言。 2. is-a has-a 关系,大家初学的时候想想,面试的时候如果有人问,吹吹,也就算了,真实地项目,能套就套,不过,哪有套的那么好的 3. 具体到这个问题,搂主想从上往下看,接口在上面,实现在下面,可惜,计算机只认识汇编而已。 4. 一定要解决这个问题,楼主自己写点 operator () 什么的,或者什么函数的,就搞定了哈。依然很完美吧。[解决办法] 探讨 这个帖子挺有意思的。 我觉得也可以这样来理解: 我们买了几团毛线,其中一部分织成毛衣,另一部分织成了毛裤。然而,我们却不能说毛线、毛衣、毛裤都是一样的东西。 vector是用来组合一组相同类型的数据的容器,组合和继承的差别就在于ISA关系不能继续。 如果这样: C/C++ codeclass CarsList { vector<Car *> m_cars; public: ... virtual BuyCar(){m_cars.push_back(new Car);} }; class B…[解决办法] 经过vector<xx>后 已经不属于父子关系了 而是两个新事物 嘿嘿[解决办法] c++的运行期类型识别,只对传指针和传引用起作用。但是你的实例中,传递的却是vector值。 vector<base*>和vector<derevied*>有继承关系吗? 如果语言作不到,那么就还一种设计思路。[解决办法] 一条内裤是内裤,四条内裤也是内裤, LZ穿了四条内裤,楼下穿了一条内裤,Lz不等于楼下[解决办法] LZ不会用函数模板吗?管他是什么车,就算不吃车,我也照单全收C/C++ codetemplate<class T>void foo( vector<T*> v ){}[解决办法] 其实derived和base可能存在继承关系,但是Vector<derived>和Vector<base>一点关系都没有 如果者都有继承关系,那更复杂的容器怎么办? RBTree<derived, base> RBTree<base,derived>你说说看是什么关系?:)[解决办法] 探讨 引用: C/C++ code一堆公共汽车应该是 #include <vector>usingstd::vector;classbase{};classderived :publicbase{};voidfoo(base*vb, size_t num) { }intmain(intargc,char*argv ) { derived*vd=newderived[10]; foo( vd,10); } 当你使用vector <>的时候,它就不只只是一堆公共汽车了,因为它除了包含一堆公共汽车外,还包含了别的属性,比如大小,容量 up 汽车经vector封装后就不是…
[解决办法] 哲学问题,什么事物都不是完美的。 肯定有两面性,既然选择了C++,这条路就要走下去,除非有了比他更好的完全能替代它的语言。 我还是初学者,感觉C++还是很强大的,我见过最强大的语言,可以做满汉全席,也可以做小虾小鱼。 哈哈,我的感觉,可能那不对。[解决办法] 哲学问题,什么事物都不是完美的。 肯定有两面性,既然选择了C++,这条路就要走下去,除非有了比他更好的完全能替代它的语言。 我还是初学者,感觉C++还是很强大的,我见过最强大的语言,可以做满汉全席,也可以做小虾小鱼。 哈哈,我的感觉,可能那不对。[解决办法] 我觉得楼主想法是很好的,很多人驳斥楼主是因为“C++语言的语法不支持这样,所以不这样”,这种驳斥有点不靠谱,楼主本来就抱怨的是C++语法么。 当然,任何时候完美的东西是不存在的,为了增加你说的这种逻辑,不知道C++需要多少的改动,不知道多少学习C++的人需要花更多的经历。而这样做除了逻辑上更美之外实际上似乎实用性也不一定特别大。很多人已经习惯了现有的东西,突然改变不是件好事情。 实际上,楼主的要求稍微违背了OO的初衷。我们继承的目的和多态的目的需要的是以基类(其实应该是接口)操作所有属于这种“类别”的对象,因此我们需要定义基类指针数组而不是派生类数组,在使用种,我们无论需要装公共汽车还是私家汽车,我们都应该用一个汽车数组,而不是分别定义一个私家车数组和公共汽车数组,因此vector<base*> vb似乎更合乎OO的原始目的[解决办法] 探讨 当然我说的不仅仅是vector的问题. 所有含有模板参数的类,都会出现这种的问题,当然C++这样做有他的目的,而且不得不这么做. 只是说明了模板这个东西本身很不适用而己.[解决办法] 探讨 不要把编译器想得这么智能 [解决办法] C++的语言问题希望由库来解决..所以lz的问题是,为什么vector不提供这样的拷贝构造函数呢? template<typename Type, typename Alloc = allocator<Type> > class vector { public: //... template<typename U> vector(const vector<U>&, Type = U()); };[解决办法] 根据62楼, 我的理解也是,楼主的疑问就是为什么vector没有提供支持内部元素间的默认或者强制类型转换的拷贝构造或者赋值函数。但我个人感觉,这个很危险。[解决办法] 抱歉,有一点错了. template <typename U> vector(const vector <U>&, Type = U()); 这不是拷贝构造函数,因为模板参数UC/C++ codetemplate<typename T, typename Alloc = allocator<Type> >class vector{public: vector(); vector(const vector&); //拷贝构造函数 template<typename U> vector(const vector<U>&, T = U()); //转换构造函数};[解决办法] java也不允许这种转换: 子类可以当成父类,子类的数组(静态或动态)可不能当成父类的数组[解决办法] 这个...我记得以前用的时候都是直接 vector<Base*> vBase; 然后vBase.push_back(new D()); 模板是编译时机制,和多态的运行时机制不一样啊。 是应用要顺应语言,而不是让语言来顺应应用,否则就不成语言了。[解决办法] 探讨 很虽然这个不是拷贝构造函数的问题. [解决办法] 探讨 引用: 抱歉,有一点错了. template <typename U> vector(const vector <U>&, Type = U()); 这不是拷贝构造函数,因为模板参数U C/C++ code template <typename T, typename Alloc = allocator <Type> > class vector { public: vector(); vector(const vector&); //拷贝构造函数 template <typename U> vector(const vector <U>&, T = U()); //转换构造函数 }; 转换…[解决办法] template最终都要转成非template的代码 估计是楼主让编译器迷茫了,不知道怎么展开代码了 [解决办法] 加入 vector 后, 根本就是两个对象,所以编译不过。 楼上说 c# 可以, 我还特意写了个测试,一样不行,下面是代码
C# code //Form 是 Control 的子类 //单个实例可以转换 Form frm = new Form(); Control ctl = frm as Control; //加入 List 对象后 //编译出错: error CS0039: Cannot convert type 'System.Collections.Generic.List<System.Windows.Forms.Form>' to 'System.Collections.Generic.List<System.Windows.Forms.Control>' //看看, 编译都通不过,显然编译器认为这两者没有继承关系 List<Form> aryForm = new List<Form>(); List<Control> aryControl = aryForm as List<Control>;[解决办法] 这个可以理解的 一堆汽车可以再增加任意一辆汽车,而一堆公共汽车不能增加其他汽车,如果要增加的话,那就变成了一堆汽车,而不是一堆公共汽车了。[解决办法] //Form 是 Control 的子类 //单个实例可以转换 Form frm = new Form(); Control ctl = frm as Control; //加入 List 对象后 //编译出错: error CS0039: Cannot convert type 'System.Collections.Generic.List<System.Windows.Forms.Form>' to 'System.Collections.Generic.List<System.Windows.Forms.Control>' //看看, 编译都通不过,显然编译器认为这两者没有继承关系 List<Form> aryForm = new List<Form>(); List<Control> aryControl = aryForm as List<Control>;[解决办法] 探讨 加入 vector 后, 根本就是两个对象,所以编译不过。 楼上说 c# 可以, 我还特意写了个测试,一样不行,下面是代码 C# code //Form 是 Control 的子类 //单个实例可以转换 Form frm = new Form(); Control ctl = frm as Control; //加入 List 对象后 //编译出错: error CS0039: Cannot convert type 'System.Collections.Generic.List<System.Wi…[解决办法] //Form 是 Control 的子类 //单个实例可以转换 Form frm = new Form(); Control ctl = frm as Control; //加入 List 对象后 //编译出错: error CS0039: Cannot convert type 'System.Collections.Generic.List<System.Windows.Forms.Form>' to 'System.Collections.Generic.List<System.Windows.Forms.Control>' //看看, 编译都通不过,显然编译器认为这两者没有继承关系 List<Form> aryForm = new List<Form>(); List<Control> aryControl = aryForm as List<Control>;[解决办法] 探讨 发了这个贴子之后,这个问题我又思考了很久. 我觉得一个更加合理而又严谨的解决方案应该是这样,如果类模板的模板的模块参数是一个仅包含虚函数的类的指针或引用,则编译器应该认为一个SomeTemplateClass < derived* >就是一个SomeTemplateClass < base* >(或者是引用). 因为在以引用或指针操作对象时,虚函数的行为是由其具体类型决定的.而非虚函数或者不用指针作为模板参数时,可以不认为他们是同一个类.[解决办法] 探讨 想让一个SomeTemplateClass < derived* >就是一个SomeTemplateClass < base* >(或者是引用). 那模板还得知道各个类之间的继承关系。是不是太强人所难了??[解决办法] 谁优要根据你用在那方面而定吧[解决办法] 探讨 这个可以理解的 一堆汽车可以再增加任意一辆汽车,而一堆公共汽车不能增加其他汽车,如果要增加的话,那就变成了一堆汽车,而不是一堆公共汽车了。 [解决办法] 还有,当你试图把C++语言的“隐式类型转换”跟OO理论中的“是”联系在一起的时候,我认为最好还是谨慎一些。 没太多想,只是觉着这两个概念之间不是等同的。 所以,还是不要说“是”,只说“类型转换”为好,那样问题才更清楚。 基于这种分析,楼主的问题应该是“为什么A可以转换成B,而由A构成的C不能转换成由B构成的D?”,那这样的例子可多了去了,再比如: struct A { int i; } a; struct B { int i; } b; a.i = b.i; ——可以 a = b; ——不可以。[解决办法]
探讨 当然,你可以说,我们可以通过程序员人为保证只在公共汽车堆中增加公共汽车,不增加其它汽车。 但这位朋友已经把存在的问题指出了——因此就算费了千辛万苦实现出来,意义也不大了。[解决办法] 同意对逻辑的讨论。公共汽车具有汽车的所有特性。而只能装公共汽车的容器却不具有装汽车的容器的所有特性,当然也就不存在继承关系了。 但是,如果子类的容器不能继承于父类的容器,势必带来很多重复代码。假设有一个存放父类对象的容器,它的代码是不能修改的。如何用最小量的代码来实现一个类似的只能存放子类的容器? 我的意见是,不要拘泥于is a和have a的概念,这两个概念往往会造成设计上的迷惑。用继承,还使用聚合,没有必然的答案。[解决办法] #include <vector> using std::vector; class base{}; class derived : public base{}; template<class T> void foo( vector<T*> v ) { base* p = static_cast<base*>(v[0]); } int main( int argc, char* argv ) { vector<derived*> vd; foo( vd ); // OK vector<int*> vd2; foo( vd2 ); // Error } 很轻松就能解决的问题,居然讨论了100楼?