發信人: eastcrusader (昨我已逝), 信區: CPlusPlus
標 題: [合集] C++容許private方法是virtual是不是一個漏洞
發信站: 水木社區 (Fri Aug 5 09:36:12 2005), 站內
☆─────────────────────────────────────☆
Arnald (墮落獸人) 于 (Wed Aug 3 10:34:22 2005) 提到:
如下面的代碼,輸出是
Base::doPublic
Derived::doPrivate
這樣豈不是可以讓讓一個子類冒充父類的private成員函數,不過好像一般private不會也沒有必要聲明為virtual
//main.cpp
#include <stdio.h>
class Base
{
public:
void virtual doPublic()
{
printf("Base::doPublic\n");
doPrivate();
}
private:
void virtual doPrivate()
{
printf("Base::doPrivate\n");
}
};
class Derived : public Base
{
private:
void doPrivate()
{
printf("Derived::doPrivate\n");
}
};
int main()
{
Base *b = new Derived();
b->doPublic();
}
☆─────────────────────────────────────☆
eastcrusader (昨我已逝) 于 (Wed Aug 3 10:59:30 2005) 提到:
這個不存在什么漏洞,也沒有冒充的問題。C++中,除了構造函數內包含的虛函數調用是
早綁定(其他還有沒有特殊情況一時記不起來),其他的虛函數調用都是晚綁定,也就是說,你在Derived 類中調用的時候,實際(this)也是函數調用的一個參數,和this指針有關的
vtable中也包含了private和protect部分的虛函數指針,vtable中的指針指向誰就是誰,責任明確,分工清楚,那來得冒充問題呢?
☆─────────────────────────────────────☆
Arnald (墮落獸人) 于 (Wed Aug 3 11:10:00 2005) 提到:
我覺得還是一個漏洞,這么說吧
有一個父類Account,有一個public方法doTransaction,有一個virtual private方法transfer(int money),doTransaction里面調用了transfer(1000),transfer的工作就是轉帳1000塊錢。
我要是有一個子類MyAccount,也定義了一個virtual private transfer(int money),但是轉帳額度是moneyX2
Account *a = new MyAccount();
a->doTransaction();
組后調用的是子類MyAccount的transfer,這樣就轉了2000塊錢,而不是本來的1000塊錢,發了:)
java可能意識到這個問題,所以private方法,默認是final的,也就是private方法就是靜態綁定的。
☆─────────────────────────────────────☆
boost (阿布) 于 (Wed Aug 3 11:21:52 2005) 提到:
要出錯也是用的人出錯。。這不是純屬活該嘛。。
☆─────────────────────────────────────☆
ilovecpp (cpp) 于 (Wed Aug 3 11:22:36 2005) 提到:
如果doTransaction()本身就是virtual function,派生類直接override它轉2000塊錢,
你是否也認為是漏洞?
☆─────────────────────────────────────☆
Arnald (墮落獸人) 于 (Wed Aug 3 11:38:29 2005) 提到:
我剛才打了比方1000塊
如果doTransaction大概是這樣的工作順序
void doTransactio(User user, int money)
{
if ( checkUserBalance(user) >= money)) //檢查用戶余額夠不夠
{
transfer(money);
}
}
checkUserBalance也是priavte的成員方法,用戶余額只有1200塊了,正常情況下doTransaction(user, 2000)肯定失敗了,但是通過重載transfer轉帳兩倍,checkUserBalance時查的是1000,實際轉了2000。
☆─────────────────────────────────────☆
ilovecpp (cpp) 于 (Wed Aug 3 11:43:45 2005) 提到:
如果doTransaction本身就是virtual function呢?我在派生類中直接把if給去掉。
你是否認為virtual function都是漏洞?
☆─────────────────────────────────────☆
jasss (robot) 于 (Wed Aug 3 12:06:15 2005) 提到:
:我覺得還是一個漏洞,這么說吧
:有一個父類Account,有一個public方法doTransaction,有一個virtual private方法
:transfer(int money),doTransaction里面調用了transfer(1000),transfer的工作就是
:轉帳1000塊錢。
:我要是有一個子類MyAccount,也定義了一個virtual private transfer(int money),但
:是轉帳額度是moneyX2
:Account *a = new MyAccount();
:a->doTransaction();
:組后調用的是子類MyAccount的transfer,這樣就轉了2000塊錢,而不是本來的1000塊錢,
:發了:)
:
Here you saw it as a serious hole, but someone else saw it as a
great "feature"... :)
Actually using virtual and private access control, we can construct
the well-known pattern -- Template Method, which was illustrated by GOF...
In your example, the doTransaction is the template method, which defines
the skeleton of an algorithm, and the virtaul private transfer function
(and other functions if necessary) is/are the concrete inner steps, and
these steps can be changed later (in derived classes) without touch the
template method itself...
So you see the private control can encapsule not only data member, but
also algorithm implementations, isn't this useful enough for you?
☆─────────────────────────────────────☆
Arnald (墮落獸人) 于 (Wed Aug 3 12:10:32 2005) 提到:
好吧,Account里面的doTransaction這樣
void doTransaction(User user, int money)
{
Token *token = getSessionToken(user, currentTime);
transfer(money, token);
}
getSessionToken是父類Account的private方法,作為子類的MyAccount,沒有辦法獲得這個token,getSessionToken返回一個類似令牌的東東驗證身份,每次操作transfer必須帶上這個token。
☆─────────────────────────────────────☆
Arnald (墮落獸人) 于 (Wed Aug 3 12:22:02 2005) 提到:
如果要實現template pattern,可以把希望子類重載的成員聲明成protected啊
Java禁止private作為virtual,一樣也可以實現template pattern
☆─────────────────────────────────────☆
ilovecpp (cpp) 于 (Wed Aug 3 12:28:24 2005) 提到:
你沒有理解我的意思。子類和父類要保持IS-A關系,子類必須滿足父類的所有invariants。
然而,overridding機制本身不能保證這一點。override掉父類的任何一個方法,都有可能
破壞invariant。所以你看,這不是private或者public的問題,這是virtual的固有問題。
就你那個例子,我的doTransaction()為什么要調用transfer()? 我可以做任何事,我
甚至可以delete this。
你需要的,是能表達invariant/constraint的語言。禁止virtual private不會帶來什么。
☆─────────────────────────────────────☆
ilovecpp (cpp) 于 (Wed Aug 3 12:50:47 2005) 提到:
That virtual function is intended to be implemented, not called, by the
derived class. Protected would be the wrong visibility, and doesn't
well communicate the intention.
☆─────────────────────────────────────☆
Arnald (墮落獸人) 于 (Wed Aug 3 13:10:40 2005) 提到:
的確沒有理解你的意思
這么說吧,一個private的方法,按說只能夠被自己這個類的方法調用
現在private方法可以是virtual的,也就導致有的情況下,private方法可以被不是這個類的方法調用
這樣,不和private的初衷矛盾嗎
☆─────────────────────────────────────☆
ilovecpp (cpp) 于 (Wed Aug 3 13:20:24 2005) 提到:
Base::private_method()自然只能被Base的方法調用。
☆─────────────────────────────────────☆
Arnald (墮落獸人) 于 (Wed Aug 3 13:43:08 2005) 提到:
我的Derived方法里的private方法,卻被Base的public方法調用了
☆─────────────────────────────────────☆
longda (longda) 于 (Wed Aug 3 13:43:27 2005) 提到:
總覺得這個討論比較沒有意義。干嘛要把你不想被子類改的聲明為virtual,還有private方法就是子類不能調用啊,變成了virtual也一樣。
☆─────────────────────────────────────☆
ilovecpp (cpp) 于 (Wed Aug 3 13:48:19 2005) 提到:
what's bad with that? it's *you* who made it "virtual".
☆─────────────────────────────────────☆
Arnald (墮落獸人) 于 (Wed Aug 3 13:55:40 2005) 提到:
ok,我看到c++和java對private方法能不能夠是virtual上有差異
java比C++出現的晚,有意的不容許private是virtual肯定是java語言的發起者覺得C++這個特性不適合java
如果程序員有意識的不個private方法弄成virtual的,兩者沒有區別
就此結貼,不再討論
☆─────────────────────────────────────☆
ilovecpp (cpp) 于 (Wed Aug 3 14:05:35 2005) 提到:
please note that java methods are by default "virtual". it must be
very bad in c++ if all your private methods are virtual, LOL.
☆─────────────────────────────────────☆
Arnald (墮落獸人) 于 (Wed Aug 3 14:15:07 2005) 提到:
In java, non-private methods are by default "virtual".
private methods are by default "final".
☆─────────────────────────────────────☆
ilovecpp (cpp) 于 (Wed Aug 3 14:38:35 2005) 提到:
Yeah, they picked "virtual" as the default, and then found that it's
a really bad choice for private methods. "Ok, let's make a special
case. Private methods should default to 'final'." But there is no
keyword "virtual" in java, so you can't override the default. oops...
Moral of the story: choose defaults really carefully.
☆─────────────────────────────────────☆
Arnald (墮落獸人) 于 (Wed Aug 3 14:57:38 2005) 提到:
So, you believe that it is a defect about Java that *private* method cannot be *virtual*?
I believe that they did it on purpose.
☆─────────────────────────────────────☆
ilovecpp (cpp) 于 (Wed Aug 3 15:07:42 2005) 提到:
Not exactly a defect. It's about favourring one less keyword (virtual) over
one more (not exceedingly important) function (overridding private methods).
Reasonable choice, if you must have non-private methods default to non-final.
Oh, you can ask Gosling. (He's coming China)
There's no D&E for Java so that's the only way...
☆─────────────────────────────────────☆
Arnald (墮落獸人) 于 (Wed Aug 3 15:27:20 2005) 提到:
OK. He must have some idea-:)
What does "D&E" stand for?
☆─────────────────────────────────────☆
supercloud (飛云) 于 (Wed Aug 3 15:43:38 2005) 提到:
a本來就是子類的指針,當然調用子類的transfer,有什么問題嗎?
☆─────────────────────────────────────☆
jasss (robot) 于 (Wed Aug 3 16:29:14 2005) 提到:
Sorry for the late response, was absent just now...
Well, of course you can make your transfer function
a protected member function, but is this really you want?
Do you really want to call the base class's transfer
function in your derived class? It is hardly possible
since the purpose of the derived class is replace the
algorithm implementation...
So, in my mind they should be kept private unless you
will call them by your design... GoF's words do not
apply everywhere...
Actually usually I try to avoid comparing Java and
cplusplus, since they have so many different respects.
when we are talking about cplusplus, the comparison
always introduces confusion in my mind...
But for your words, IMO, it is a serious problem of
Java language(of course, YMMV). For example, without
virtual private method, it is difficult to simulate the
design-by-contract policy...
And BTW, Template Method does not require you put your
algorithms into private/protected regions, but is this
really what you want?
☆─────────────────────────────────────☆
jasss (robot) 于 (Wed Aug 3 16:33:22 2005) 提到:
But for cplusplus, access control and overriding are
totally independent/orthogonal concepts. As you know,
access control specifies who can call the controlled
functions, however virtual functions *are* parts of
the interface by design, and the derived classes should
respect this fact while using cplusplus...
☆─────────────────────────────────────☆
goer (玨) 于 (Wed Aug 3 16:42:39 2005) 提到:
繼承來的方法應該也算是這個類的方法吧?
☆─────────────────────────────────────☆
jasss (robot) 于 (Wed Aug 3 16:47:24 2005) 提到:
LOL, actually this issue had been discussing for a long time...
Actually I know several opinions and its
supporters(not accurate enough, maybe) :
1. This should be removed at the language level.
(Arnald)
2. You can do it, but it is not very useful and
use it at your own risk.
(Scott Mayer)
3. This is useful, use it whenever it fits.
(Bjarne Stroustrup)
4. virtual function should be private by default.
and make the interface non-virtual.
(Herb Sutter)
Actually this question is really a good question/topic,
since the understanding for this issue will dominate
the design policy...
☆─────────────────────────────────────☆
hlyu76 (魚啊魚) 于 (Wed Aug 3 18:49:20 2005) 提到:
private virtual 表明這是一個內部實現,不允許子類或其他類調用(如果是protected的,無法保證子類不調用這個實現)。
但給了子類一個提供自己實現的途徑(這個實現無法調用父類的實現)。
☆─────────────────────────────────────☆
longda (longda) 于 (Wed Aug 3 23:05:08 2005) 提到:
這個靜態綁定好像有問題啊,java里有靜態綁定的函數調用嗎?
☆─────────────────────────────────────☆
ironcool (除了我還能是誰) 于 (Thu Aug 4 11:04:01 2005) 提到:
我覺得就好像自己和自己做迷藏
程序的行為,當然是自己負責啊