class A
{
public:
void f(){cout << "From Class A: SHIT!" << endl;}
};
class B
{
public:
void g(){cout << "From Class B:" << endl;}
};
void callf(A&a) {a.f();}
void callg(B&b) {b.g();}
上面是两个类class A和class B,还有两个全局函数:callf(&A)和callg(&B)。分别在这两个全局函数中调用f()和g(),要求实现一个类C,分别用public和protected继承A和B。然后实例化C,在主函数中以C的实例作为参数,调用两个全局函数。这样能否通过编译?如果不能, 不改变程序的继承结构和全局函数, 修改程序, 实现要求。
按照要求,对于第一问,可以写出如下代码:
#include<iostream>
using namespace std;
class A
{
public:
void f(){cout << "From Class A: SHIT!" << endl;}
};
class B
{
public:
void g(){cout << "From Class B:" << endl;}
};
void callf(A&a) {a.f();}
void callg(B&b) {b.g();}
class C:public A, protected B{};
int main()
{
C c;
callf(c);
callg(c);
return 0;
}
这个代码编译自然是通不过,在callg(c)这个地方,保护继承的话,派生类对象肯定没法调用基类的方法了。那么要怎么改呢?先谢谢了~
61 个解决方案
#1
#2
设为友元试试。
#3
这样?
还是不行的。。。
void callf(A&a) {a.f();}
void callg(B&b) {b.g();}
class C:public A, protected B
{
friend void callg(B & b);
};
还是不行的。。。
#4
不过友元函数根本就不是成员函数,这和楼主的意图应该有所不同了。
#5
办法肯定有,不过应该是不太常规的做法。
#6
下面这样就可以满足楼主的设想:
思路是这样的:
1. 将B中的函数改成虚函数,那么在C中就会产生一个虚函数表指针。C的内存布局大致是这样的:
+--------------------- Class C
| +------------------- Class B
| | {vfptr}
| +-------------------
| +------------------- Class A
| |
| +-------------------
+-----------------------
2. 从上图中可以看出B这个Suboject在C对象中的开始处,也就是C对象的地址和其中的B这个Suboject的地址是一样的,因此只要将C对象的地址,转换成B类型的地址即可。
3. 但正常的转型会报错,因为比如dynamic_cast转型的时候会进行语法检查,它会认为将C*转型为B*是不可以的(因为C是protected继承B的)。但我们很清楚地知道自己要干什么,因此,我们可以用reinterpret_cast进行强制转换,这是没有什么问题的。
4. 转换成功后,就可以随意处理了。使用类似的方法,在外面,调用一个类中的私有成员变量都是可以做到的。
#include<iostream>
using namespace std;
class A
{
public:
void f(){cout << "From Class A: SHIT!" << endl;}
};
class B
{
public:
virtual void g() // 改为virtual
{
cout << "From Class B:" << endl;
}
};
void callf(A* a) {a->f();} // 改用指针
void callg(B* b) {b->g();} // 改用指针
class C:public A, protected B
{
};
int main()
{
C* c = new C;
callf(c);
callg(reinterpret_cast<B*>(c)); // 这个地方要进行强制转换
return 0;
}
思路是这样的:
1. 将B中的函数改成虚函数,那么在C中就会产生一个虚函数表指针。C的内存布局大致是这样的:
+--------------------- Class C
| +------------------- Class B
| | {vfptr}
| +-------------------
| +------------------- Class A
| |
| +-------------------
+-----------------------
2. 从上图中可以看出B这个Suboject在C对象中的开始处,也就是C对象的地址和其中的B这个Suboject的地址是一样的,因此只要将C对象的地址,转换成B类型的地址即可。
3. 但正常的转型会报错,因为比如dynamic_cast转型的时候会进行语法检查,它会认为将C*转型为B*是不可以的(因为C是protected继承B的)。但我们很清楚地知道自己要干什么,因此,我们可以用reinterpret_cast进行强制转换,这是没有什么问题的。
4. 转换成功后,就可以随意处理了。使用类似的方法,在外面,调用一个类中的私有成员变量都是可以做到的。
#7
确实,换成友元函数就和访问类成员对象不一致了。
不太常规的做法,可以给一个么~
讲得很详细,真是太谢谢了!这个代码编译可以通过,只是好像修改了全局函数,有点不符合题目要求。如果在全局函数里面还是按照原来那样,不改成指针,能不能做呢?
感谢大家的热心回答~
#8
用reinterpret_cast的话,太暴力了点。
如果reinterpret_cast也算,又何必改那么多?只需要改一句就行了。把main函数中的:
callg(c);
这句改成:
callg(reinterpret_cast<B&>(c));
其它都不用动。
如果reinterpret_cast也算,又何必改那么多?只需要改一句就行了。把main函数中的:
callg(c);
这句改成:
callg(reinterpret_cast<B&>(c));
其它都不用动。
#9
您说的对。最开始想整个虚函数好让楼主看得清楚点。
#10
按照8楼大佬的建议,修改如下,只改动了一个地方(注释处),道理还是和6楼给出的是一样的。
#include<iostream>
using namespace std;
class A
{
public:
void f(){cout << "From Class A: SHIT!" << endl;}
};
class B
{
public:
void g()
{
cout << "From Class B:" << endl;
}
};
void callf(A a) {a.f();}
void callg(B b) {b.g();}
class C: public A, protected B
{
};
int main()
{
C c;
callf(c);
callg(reinterpret_cast<B&>(c)); // 这个地方要进行强制转换
return 0;
}
#11
哦哦哦。
另外刚刚我也想了一下,既然用上了reinterpret_cast,恐怕用虚函数也不见得安全,比如当函数中访问对象成员的时候。
当然,楼主的两个函数实现在多数编译器上问题不大,因为只是调用全局的cout打印两行语句。
#12
另外,友元可以的。不要friend callg,应该friend main。
#13
看得真透彻!
不安全的话,如果用完以后再reinterpret_cast回去行不行呢?
确实,这样改就可以了。。。但是有点不太明白的,为什么friend callg不行而friend main可以呢?
感谢两位高手的热心解答,让我学到不少:)
#14
之所以说不安全,是指这样的用法本身就有可能不安全(尤其当B::g()实现较为复杂的时候),而不是说用完了之后怎样怎样。
因为从概念上讲,类型的隐式转换发生在main函数内部向callg传递参数的时候,而不是发生在callg函数“内部”。于是,需要了解“C从B派生”这一小秘密的是main,而不是callg。
#15
ss A
{
public:
void f(){cout << "From Class A: SHIT!" << endl;}
};
class B
{
{
public:
void f(){cout << "From Class A: SHIT!" << endl;}
};
class B
{
#16
Good talk
#17
学习学习
#18
支持下
#19
不明白楼主为什么一定要protected继承而又需要在callg调用这个成员函数呢
有很多问题是由于不合理的设计造成的
有很多问题是由于不合理的设计造成的
#20
学习了!
原来是这样!在main中进行类型转换,然后再传入到callg中去。
那么进行类型的隐式转换时,如果main不是C类的友元,则其不能使用C类从B类继承的方法,于是“不允许对不可访问的基类进行转换”——本来设计成protected继承就是为了不让派生类实例使用基类的成员,如果允许转换,那这个设计就没有用了。而当main成为C类的友元后,可以访问C类从B类继承的方法了,这个“友元”就是告诉编译器“我(main)要用到B类的方法”,让编译器确信coder知道自己在干什么,从而允许派生类对象转换为原本不可访问的基类。使用reinterpret_cast也是起到类似的作用吧。
将main作为B类的友元并没有用,因为在main中需要访问的是C类的实例而不是B类的实例。
这是个人对解释的一点理解,不知道有没有错误的地方,若有还请指正!
#21
这其实是别人问我的一道题目,一本C++书上的。。。呵呵,实际中应该不能这样做吧,这样做了就确实是个不好的设计。
单纯只是个题目,呵呵。不过也好,学到了很多东西~
#22
学习一下
#23
iostream.h
#24
也可以重载函数callg()
class A
{
public:
void f(){cout << "From Class A: SHIT!" << endl;}
};
class B
{
public:
void g(){cout << "From Class B:" << endl;}
};
void callf(A&a) {a.f();}
void callg(B&b) {b.g();}
class C:public A, protected B
{
friend void callg(C&);
};
void callg(C&c) {static_cast<B&>(c).g();} //重载
int main()
{
C c;
callf(c);
callg(c);
return 0;
}
#25
太好了,学习中,谢谢
#26
#27
上面的重载函数写错了,应该是:
void callg(C&c) //重载
{
callg(static_cast<B&>(c));
}
#28
顶一个吧......呵呵
#29
学习一下
#30
学习一下学习一下学习一下
#31
学习一下学习一下学习一下
#32
这样重载也不行,编译还是不能过,提示和原来同样的错误。
不过这样重载之后,感觉设置callg为C类的友元应该可以,结果编译提示:
error C2440: 'static_cast' : cannot convert from 'class C' to 'class B &'
static_cast to reference can only be used for valid initializations or for lvalue casts between related classes
Error executing cl.exe.
看来用static_cast是不行的。改成reinterpret_cast以后可以了,不过这就回到前面最开始提到的方法了,呵呵。
#33
我想赚积分 是通过回帖的不
#34
学习了,mark。
#35
顶……
#36
学习 学习 学习 学习
#37
虽然不懂 但是支持
#38
你确定把callg(C&)设为C的友元了?我在vs2010上测试可以通过编译啊,
我猜你用的编译器是vc++6.0,它有很多地方不符合c++标准。
#39
vs2010也试了,只要是用static_cast就不行,即使两个全局函数callg(原来的和重载的)都设置为C类的友元,也不行,单独一个设置为友元,也不行。
能不能看看你的代码?
#40
强制转换太粗鲁了吧 友元多科学
#41
以下程序在vs2010,vs2008,devc++,下测试,均可正常通过编译.
#include<iostream>
using namespace std;
class A
{
public:
void f(){cout << "From Class A: SHIT!" << endl;}
};
class B
{
public:
void g(){cout << "From Class B:" << endl;}
};
void callf(A&a) {a.f();}
void callg(B&b) {b.g();}
class C:public A, protected B
{
friend void callg(C& c) //重载并声明为友元
{
callg(static_cast<B&>(c));
}
};
int main()
{
C c;
callf(c);
callg(c);
return 0;
}
#42
你对继承的理解不完全准确,而主要原因是把以下几件事情搞混了:
(1)子类对父类成员的访问权限跟如何继承没有任何关系,“子类可以访问父类的public和protected成员,不可以访问父类的private成员”——这句话对任何一种继承都是成立的。
(2)继承修饰符影响着谁可以知道“继承”这件事。public继承大家都知道,有点像“法定继承人”,因此,任何代码都可以把子类的引用(或指针)直接转换为父类。也因为这个原因,public继承常用来表达设计中所谓的“is-a”关系。private继承则有点像“私生子”,除了子类自己,没有人知道这层关系,也因此,除了子类自己的代码之外,没有其它人知道自己还有个父亲,于是也就没有其它人可以做相应的类型转换。为此,私有继承常用于表达非“is-a”的关系,这种情况下子类只是借用父类的某些实现细节。protected继承则有点特殊,外界同样不知道这层关系,但家族内部的子孙们可以知道,有点像“自家知道就行了,不许外扬”的意思,于是子孙们是可以做这种向上转型,其它代码则不可以。因为这种特殊性,protected继承在实际中用得很少。
(3)还需要补充一点,由于“继承关系”的可见性受到了影响,那么继承来的财产的可见性也必然受到影响。比如一个成员变量或成员函数,在父类中本来是public的,被某个子类protected继承之后,对子类来讲,这个成员就相当于protected成员了——继承是继承到了,但权限变了。具体的规则教材上都会讲的。
#43
你的代码确实没问题,我发现我自己的问题了。。我先前直接把重载的函数粘贴过去,结果放到了C类定义的前面——前面加上C类的声明,这样一来,函数出现在被声明为友元之前,导致使用static_cast的地方编译报错。将重载函数的定义放到C类定义之后,编译就通过了。
查了一下前面编译器提示的那个错误:
error C2440: 'static_cast' : cannot convert from 'class C' to 'class B &'
static_cast to reference can only be used for valid initializations or for lvalue casts between related classes
在MSDN中有对error C2440的解释: http://msdn.microsoft.com/en-us/library/sy5tsf8z(v=VS.100).aspx
关键的一点就是:Incompatible calling conventions for UDT return value message(UDT是指User Defined Type,即用户定义类型)
看了解释,我的理解是,前后的“调用约定(calling convention)”不一致导致错误,关于这个不是太明白,看了本地的中文版MSDN,大致感觉是:在前向声明中编译器假设使用C++调用约定,而等结构体被读完以后才发现默认是使用C调用约定,这样前后就不一致,在后面的代码中会出现问题。
具体还请参见MSDN上内容,我这里说的很不准确,也很模糊。
要解决由于不兼容的调用约定而产生的 C2440,MSDN给出了一个建议:To resolve C2440 because of incompatible calling conventions, declare functions that return a UDT after the UDT definition.
导致C2440的原因有多种,我觉得MSDN列出的这一个是最符合我的代码的情况。我理解为static_cast和C类使用的调用约定冲突了,不知道这样说对不对。
如果不是第一条,我觉得那也许是第三条:C2440 can also occur for an incorrect use of a user-defined conversion.
看了朋友的解释,真是有点恍然大悟的感觉,解释得太好了!让我对于继承又有了更好的理解。
感谢大家的指点!
#44
mark
#45
非常不错。
#46
占楼学习
#47
好像上面代码都要转换哈~ 实际上加个重载函数就可以优雅的解决了:
#include<iostream>
using namespace std;
class A {
public:
void f(){cout << "From Class A: SHIT!" << endl;}
};
class B {
public:
void g(){cout << "From Class B:" << endl;}
};
void callf(A&a) { a.f(); }
void callg(B&b) { b.g(); }
class C:public A, protected B {
friend void callg(C&); // 重载callg
};
void callg(C &c) {
callg(B(c)); // 指定调用 callg(B&)
}
int main() {
C c;
callf(c);
callg(c);
return 0;
}
#48
学习了。。。。
#49
顶一个。。。。。。。。。
#50
恩,重载的callg作为C类的友元,允许C类到B类的转换,在main函数中调用的是重载的函数。
就像中间搭了一座桥,是吧,呵呵~
#1
#2
设为友元试试。
#3
这样?
还是不行的。。。
void callf(A&a) {a.f();}
void callg(B&b) {b.g();}
class C:public A, protected B
{
friend void callg(B & b);
};
还是不行的。。。
#4
不过友元函数根本就不是成员函数,这和楼主的意图应该有所不同了。
#5
办法肯定有,不过应该是不太常规的做法。
#6
下面这样就可以满足楼主的设想:
思路是这样的:
1. 将B中的函数改成虚函数,那么在C中就会产生一个虚函数表指针。C的内存布局大致是这样的:
+--------------------- Class C
| +------------------- Class B
| | {vfptr}
| +-------------------
| +------------------- Class A
| |
| +-------------------
+-----------------------
2. 从上图中可以看出B这个Suboject在C对象中的开始处,也就是C对象的地址和其中的B这个Suboject的地址是一样的,因此只要将C对象的地址,转换成B类型的地址即可。
3. 但正常的转型会报错,因为比如dynamic_cast转型的时候会进行语法检查,它会认为将C*转型为B*是不可以的(因为C是protected继承B的)。但我们很清楚地知道自己要干什么,因此,我们可以用reinterpret_cast进行强制转换,这是没有什么问题的。
4. 转换成功后,就可以随意处理了。使用类似的方法,在外面,调用一个类中的私有成员变量都是可以做到的。
#include<iostream>
using namespace std;
class A
{
public:
void f(){cout << "From Class A: SHIT!" << endl;}
};
class B
{
public:
virtual void g() // 改为virtual
{
cout << "From Class B:" << endl;
}
};
void callf(A* a) {a->f();} // 改用指针
void callg(B* b) {b->g();} // 改用指针
class C:public A, protected B
{
};
int main()
{
C* c = new C;
callf(c);
callg(reinterpret_cast<B*>(c)); // 这个地方要进行强制转换
return 0;
}
思路是这样的:
1. 将B中的函数改成虚函数,那么在C中就会产生一个虚函数表指针。C的内存布局大致是这样的:
+--------------------- Class C
| +------------------- Class B
| | {vfptr}
| +-------------------
| +------------------- Class A
| |
| +-------------------
+-----------------------
2. 从上图中可以看出B这个Suboject在C对象中的开始处,也就是C对象的地址和其中的B这个Suboject的地址是一样的,因此只要将C对象的地址,转换成B类型的地址即可。
3. 但正常的转型会报错,因为比如dynamic_cast转型的时候会进行语法检查,它会认为将C*转型为B*是不可以的(因为C是protected继承B的)。但我们很清楚地知道自己要干什么,因此,我们可以用reinterpret_cast进行强制转换,这是没有什么问题的。
4. 转换成功后,就可以随意处理了。使用类似的方法,在外面,调用一个类中的私有成员变量都是可以做到的。
#7
确实,换成友元函数就和访问类成员对象不一致了。
不太常规的做法,可以给一个么~
讲得很详细,真是太谢谢了!这个代码编译可以通过,只是好像修改了全局函数,有点不符合题目要求。如果在全局函数里面还是按照原来那样,不改成指针,能不能做呢?
感谢大家的热心回答~
#8
用reinterpret_cast的话,太暴力了点。
如果reinterpret_cast也算,又何必改那么多?只需要改一句就行了。把main函数中的:
callg(c);
这句改成:
callg(reinterpret_cast<B&>(c));
其它都不用动。
如果reinterpret_cast也算,又何必改那么多?只需要改一句就行了。把main函数中的:
callg(c);
这句改成:
callg(reinterpret_cast<B&>(c));
其它都不用动。
#9
您说的对。最开始想整个虚函数好让楼主看得清楚点。
#10
按照8楼大佬的建议,修改如下,只改动了一个地方(注释处),道理还是和6楼给出的是一样的。
#include<iostream>
using namespace std;
class A
{
public:
void f(){cout << "From Class A: SHIT!" << endl;}
};
class B
{
public:
void g()
{
cout << "From Class B:" << endl;
}
};
void callf(A a) {a.f();}
void callg(B b) {b.g();}
class C: public A, protected B
{
};
int main()
{
C c;
callf(c);
callg(reinterpret_cast<B&>(c)); // 这个地方要进行强制转换
return 0;
}
#11
哦哦哦。
另外刚刚我也想了一下,既然用上了reinterpret_cast,恐怕用虚函数也不见得安全,比如当函数中访问对象成员的时候。
当然,楼主的两个函数实现在多数编译器上问题不大,因为只是调用全局的cout打印两行语句。
#12
另外,友元可以的。不要friend callg,应该friend main。
#13
看得真透彻!
不安全的话,如果用完以后再reinterpret_cast回去行不行呢?
确实,这样改就可以了。。。但是有点不太明白的,为什么friend callg不行而friend main可以呢?
感谢两位高手的热心解答,让我学到不少:)
#14
之所以说不安全,是指这样的用法本身就有可能不安全(尤其当B::g()实现较为复杂的时候),而不是说用完了之后怎样怎样。
因为从概念上讲,类型的隐式转换发生在main函数内部向callg传递参数的时候,而不是发生在callg函数“内部”。于是,需要了解“C从B派生”这一小秘密的是main,而不是callg。
#15
ss A
{
public:
void f(){cout << "From Class A: SHIT!" << endl;}
};
class B
{
{
public:
void f(){cout << "From Class A: SHIT!" << endl;}
};
class B
{
#16
Good talk
#17
学习学习
#18
支持下
#19
不明白楼主为什么一定要protected继承而又需要在callg调用这个成员函数呢
有很多问题是由于不合理的设计造成的
有很多问题是由于不合理的设计造成的
#20
学习了!
原来是这样!在main中进行类型转换,然后再传入到callg中去。
那么进行类型的隐式转换时,如果main不是C类的友元,则其不能使用C类从B类继承的方法,于是“不允许对不可访问的基类进行转换”——本来设计成protected继承就是为了不让派生类实例使用基类的成员,如果允许转换,那这个设计就没有用了。而当main成为C类的友元后,可以访问C类从B类继承的方法了,这个“友元”就是告诉编译器“我(main)要用到B类的方法”,让编译器确信coder知道自己在干什么,从而允许派生类对象转换为原本不可访问的基类。使用reinterpret_cast也是起到类似的作用吧。
将main作为B类的友元并没有用,因为在main中需要访问的是C类的实例而不是B类的实例。
这是个人对解释的一点理解,不知道有没有错误的地方,若有还请指正!
#21
这其实是别人问我的一道题目,一本C++书上的。。。呵呵,实际中应该不能这样做吧,这样做了就确实是个不好的设计。
单纯只是个题目,呵呵。不过也好,学到了很多东西~
#22
学习一下
#23
iostream.h
#24
也可以重载函数callg()
class A
{
public:
void f(){cout << "From Class A: SHIT!" << endl;}
};
class B
{
public:
void g(){cout << "From Class B:" << endl;}
};
void callf(A&a) {a.f();}
void callg(B&b) {b.g();}
class C:public A, protected B
{
friend void callg(C&);
};
void callg(C&c) {static_cast<B&>(c).g();} //重载
int main()
{
C c;
callf(c);
callg(c);
return 0;
}
#25
太好了,学习中,谢谢
#26
#27
上面的重载函数写错了,应该是:
void callg(C&c) //重载
{
callg(static_cast<B&>(c));
}
#28
顶一个吧......呵呵
#29
学习一下
#30
学习一下学习一下学习一下
#31
学习一下学习一下学习一下
#32
这样重载也不行,编译还是不能过,提示和原来同样的错误。
不过这样重载之后,感觉设置callg为C类的友元应该可以,结果编译提示:
error C2440: 'static_cast' : cannot convert from 'class C' to 'class B &'
static_cast to reference can only be used for valid initializations or for lvalue casts between related classes
Error executing cl.exe.
看来用static_cast是不行的。改成reinterpret_cast以后可以了,不过这就回到前面最开始提到的方法了,呵呵。
#33
我想赚积分 是通过回帖的不
#34
学习了,mark。
#35
顶……
#36
学习 学习 学习 学习
#37
虽然不懂 但是支持
#38
你确定把callg(C&)设为C的友元了?我在vs2010上测试可以通过编译啊,
我猜你用的编译器是vc++6.0,它有很多地方不符合c++标准。
#39
vs2010也试了,只要是用static_cast就不行,即使两个全局函数callg(原来的和重载的)都设置为C类的友元,也不行,单独一个设置为友元,也不行。
能不能看看你的代码?
#40
强制转换太粗鲁了吧 友元多科学
#41
以下程序在vs2010,vs2008,devc++,下测试,均可正常通过编译.
#include<iostream>
using namespace std;
class A
{
public:
void f(){cout << "From Class A: SHIT!" << endl;}
};
class B
{
public:
void g(){cout << "From Class B:" << endl;}
};
void callf(A&a) {a.f();}
void callg(B&b) {b.g();}
class C:public A, protected B
{
friend void callg(C& c) //重载并声明为友元
{
callg(static_cast<B&>(c));
}
};
int main()
{
C c;
callf(c);
callg(c);
return 0;
}
#42
你对继承的理解不完全准确,而主要原因是把以下几件事情搞混了:
(1)子类对父类成员的访问权限跟如何继承没有任何关系,“子类可以访问父类的public和protected成员,不可以访问父类的private成员”——这句话对任何一种继承都是成立的。
(2)继承修饰符影响着谁可以知道“继承”这件事。public继承大家都知道,有点像“法定继承人”,因此,任何代码都可以把子类的引用(或指针)直接转换为父类。也因为这个原因,public继承常用来表达设计中所谓的“is-a”关系。private继承则有点像“私生子”,除了子类自己,没有人知道这层关系,也因此,除了子类自己的代码之外,没有其它人知道自己还有个父亲,于是也就没有其它人可以做相应的类型转换。为此,私有继承常用于表达非“is-a”的关系,这种情况下子类只是借用父类的某些实现细节。protected继承则有点特殊,外界同样不知道这层关系,但家族内部的子孙们可以知道,有点像“自家知道就行了,不许外扬”的意思,于是子孙们是可以做这种向上转型,其它代码则不可以。因为这种特殊性,protected继承在实际中用得很少。
(3)还需要补充一点,由于“继承关系”的可见性受到了影响,那么继承来的财产的可见性也必然受到影响。比如一个成员变量或成员函数,在父类中本来是public的,被某个子类protected继承之后,对子类来讲,这个成员就相当于protected成员了——继承是继承到了,但权限变了。具体的规则教材上都会讲的。
#43
你的代码确实没问题,我发现我自己的问题了。。我先前直接把重载的函数粘贴过去,结果放到了C类定义的前面——前面加上C类的声明,这样一来,函数出现在被声明为友元之前,导致使用static_cast的地方编译报错。将重载函数的定义放到C类定义之后,编译就通过了。
查了一下前面编译器提示的那个错误:
error C2440: 'static_cast' : cannot convert from 'class C' to 'class B &'
static_cast to reference can only be used for valid initializations or for lvalue casts between related classes
在MSDN中有对error C2440的解释: http://msdn.microsoft.com/en-us/library/sy5tsf8z(v=VS.100).aspx
关键的一点就是:Incompatible calling conventions for UDT return value message(UDT是指User Defined Type,即用户定义类型)
看了解释,我的理解是,前后的“调用约定(calling convention)”不一致导致错误,关于这个不是太明白,看了本地的中文版MSDN,大致感觉是:在前向声明中编译器假设使用C++调用约定,而等结构体被读完以后才发现默认是使用C调用约定,这样前后就不一致,在后面的代码中会出现问题。
具体还请参见MSDN上内容,我这里说的很不准确,也很模糊。
要解决由于不兼容的调用约定而产生的 C2440,MSDN给出了一个建议:To resolve C2440 because of incompatible calling conventions, declare functions that return a UDT after the UDT definition.
导致C2440的原因有多种,我觉得MSDN列出的这一个是最符合我的代码的情况。我理解为static_cast和C类使用的调用约定冲突了,不知道这样说对不对。
如果不是第一条,我觉得那也许是第三条:C2440 can also occur for an incorrect use of a user-defined conversion.
看了朋友的解释,真是有点恍然大悟的感觉,解释得太好了!让我对于继承又有了更好的理解。
感谢大家的指点!
#44
mark
#45
非常不错。
#46
占楼学习
#47
好像上面代码都要转换哈~ 实际上加个重载函数就可以优雅的解决了:
#include<iostream>
using namespace std;
class A {
public:
void f(){cout << "From Class A: SHIT!" << endl;}
};
class B {
public:
void g(){cout << "From Class B:" << endl;}
};
void callf(A&a) { a.f(); }
void callg(B&b) { b.g(); }
class C:public A, protected B {
friend void callg(C&); // 重载callg
};
void callg(C &c) {
callg(B(c)); // 指定调用 callg(B&)
}
int main() {
C c;
callf(c);
callg(c);
return 0;
}
#48
学习了。。。。
#49
顶一个。。。。。。。。。
#50
恩,重载的callg作为C类的友元,允许C类到B类的转换,在main函数中调用的是重载的函数。
就像中间搭了一座桥,是吧,呵呵~