在鑽石繼承存在的情況下防止多餘的函數調用有什麼好的策略?具體來說,假設我們有一個程序:在鑽石繼承存在的情況下防止多餘的函數調用
#include <iostream>
struct A {
int a;
A(int a_) : a(a_) {}
virtual void print() {
std::cout << "a: " << a << std::endl;
}
};
struct B : virtual public A {
int b;
B(int a_,int b_) : A(a_), b(b_) {}
virtual void print() {
A::print();
std::cout << "b: " << b << std::endl;
}
};
struct C : virtual public A {
int c;
C(int a_,int c_) : A(a_), c(c_) {}
virtual void print() {
A::print();
std::cout << "c: " << c << std::endl;
}
};
struct D : public B,public C {
int d;
D(int a_,int b_,int c_,int d_) : A(a_), B(a_,b_), C(a_,c_), d(d_) {}
void print() {
B::print();
C::print();
std::cout << "d: " << d << std::endl;
}
};
int main() {
D d(1,2,3,4);
d.print();
}
當我們調用d.print(),我們得到:
a: 1
b: 2
a: 1
c: 3
d: 4
其中一個已經打印了兩次。有沒有防止這種情況的好方法?當然,我們可以手動接線連接與像代碼:
#include <iostream>
struct A {
int a;
A(int a_) : a(a_) {}
virtual void print_() {
std::cout << "a: " << a << std::endl;
}
virtual void print() {
A::print_();
}
};
struct B : virtual public A {
int b;
B(int a_,int b_) : A(a_), b(b_) {}
virtual void print_() {
std::cout << "b: " << b << std::endl;
}
virtual void print() {
A::print_();
B::print_();
}
};
struct C : virtual public A {
int c;
C(int a_,int c_) : A(a_), c(c_) {}
virtual void print_() {
std::cout << "c: " << c << std::endl;
}
virtual void print() {
A::print_();
C::print_();
}
};
struct D : public B,public C {
int d;
D(int a_,int b_,int c_,int d_) : A(a_), B(a_,b_), C(a_,c_), d(d_) {}
virtual void print_() {
std::cout << "d: " << d << std::endl;
}
virtual void print() {
A::print_();
B::print_();
C::print_();
D::print_();
}
};
int main() {
D d(1,2,3,4);
d.print();
}
其正確輸出
a: 1
b: 2
c: 3
d: 4
,但我想知道是否有更好的方法。就出現這種情況而言,設想對象A,B,C和D的情況很複雜,需要能夠將自己寫入磁盤。我們只想爲每個A,B,C和D編寫輸出代碼,重要的是D不要兩次寫入有關A的信息。
< ---編輯--->
這裏有兩個解決問題的辦法,但它們仍然是一種鈍。第一個來自Cristian,涉及設置A是否已經打印的標誌
#include <iostream>
struct A {
int a;
bool have_printed;
A(int a_) : have_printed(false), a(a_) {}
virtual void print() {
if(have_printed) return;
std::cout << "a: " << a << std::endl;
have_printed=true;
}
void clear() {
have_printed=false;
}
};
struct B : virtual public A {
int b;
B(int a_,int b_) : A(a_), b(b_) {}
virtual void print() {
A::print();
std::cout << "b: " << b << std::endl;
}
};
struct C : virtual public A {
int c;
C(int a_,int c_) : A(a_), c(c_) {}
virtual void print() {
A::print();
std::cout << "c: " << c << std::endl;
}
};
struct D : public B,public C {
int d;
D(int a_,int b_,int c_,int d_) : A(a_), B(a_,b_), C(a_,c_), d(d_) {}
void print() {
B::print();
C::print();
std::cout << "d: " << d << std::endl;
}
};
int main() {
D d(1,2,3,4);
d.clear();
d.print();
}
正確輸出。第二種方法更爲複雜,但可能會使結構增長。基本上,我們將打印機從課程中分離出來,然後在每個對象內註冊打印機列表。當我們想要打印時,我們遍歷打印機列表,然後給我們正確的輸出。我覺得這佔用過多的機械,但我會包括案件別人得到一個更好的主意:
// A simple unary function. Technically, the stl version doesn't require
// the operator
template <typename A,typename B>
struct unary {
virtual B operator() (A a) {};
};
struct A {
// Data
int a;
// A list of pointers to unary functions. We need pointers to unary
// functions rather than unary functions since we need the printer
// to be polymorphic.
std::list < unary<A*,void>* > printers;
A(int a_);
// We actually end up allocating memory for the printers, which is held
// internally. Here, we free that memory.
~A() {
for(std::list < unary<A*,void>* >::iterator printer
=printers.begin();
printer != printers.end();
printer++
)
delete (*printer);
}
private:
// Require for the dynamic cast used later
virtual void ___dummy() {};
};
// Prints out the data for A
struct A_Printer : public unary<A*,void>{
void operator() (A* a) {
std::cout << "a: " << a->a << std::endl;
}
};
// Adds the printer for a to the list of printers
A::A(int a_) : a(a_) {
printers.push_back(new A_Printer());
}
// Now that the structure is setup, we just need to define the data for b,
// it's printer, and then register the printer with the rest
struct B : virtual public A {
int b;
B(int a_,int b_);
};
struct B_Printer : public unary<A*,void>{
void operator() (A* b) {
std::cout << "b: " << dynamic_cast <B*>(b)->b << std::endl;
}
};
B::B(int a_,int b_) : A(a_), b(b_) {
printers.push_back(new B_Printer());
}
// See the discussion for B
struct C : virtual public A {
int c;
C(int a_,int c_);
};
struct C_Printer : public unary<A*,void>{
void operator() (A* c) {
std::cout << "c: " << dynamic_cast <C*>(c)->c << std::endl;
}
};
C::C(int a_,int c_) : A(a_), c(c_) {
printers.push_back(new C_Printer());
}
// See the discussion for B
struct D : public B,public C {
int d;
D(int a_,int b_,int c_,int d_);
};
struct D_Printer : public unary<A*,void>{
void operator() (A* d) {
std::cout << "d: " << dynamic_cast <D*>(d)->d << std::endl;
}
};
D::D(int a_,int b_,int c_,int d_) : A(a_), B(a_,b_), C(a_,c_), d(d_) {
printers.push_back(new D_Printer());
}
// This actually prints everything. Basically, we iterate over the printers
// and call each one in term on the input.
void print(A* a) {
for(std::list < unary<A*,void>* >::iterator printer
=a->printers.begin();
printer != a->printers.end();
printer++
)
(*(*printer))(a);
}
int main() {
D d(1,2,3,4);
// This should print 1,2,3,4
print(&d);
}
< --- EDIT 2 --->
tmpearce有一個好主意積累在組裝之前,所有信息都在散列表中。通過這種方式,可以檢查個人信息是否已經創建並防止冗餘。我認爲這是一個好主意,可以很容易地組裝信息。如果情況並非如此,則可能會發生輕微的變化,這結合了tmpearce和Cristian的想法。在這裏,我們傳遞一組(或散列表,或其他)來跟蹤函數是否被調用。通過這種方式,我們可以檢查某個函數是否已經被計算出來。它不需要永久的狀態,所以它應該是安全的多次調用:
#include <iostream>
#include <set>
struct A {
int a;
A(int a_) : a(a_) {}
virtual void print_(std::set <std::string>& computed) {
if(computed.count("A") > 0) return;
computed.insert("A");
std::cout << "a: " << a << std::endl;
}
void print() {
std::set <std::string> computed;
print_(computed);
}
};
struct B : virtual public A {
int b;
B(int a_,int b_) : A(a_), b(b_) {}
virtual void print_(std::set <std::string>& computed) {
A::print_(computed);
if(computed.count("B") > 0) return;
computed.insert("B");
std::cout << "b: " << b << std::endl;
}
};
struct C : virtual public A {
int c;
C(int a_,int c_) : A(a_), c(c_) {}
virtual void print_(std::set <std::string>& computed) {
A::print_(computed);
if(computed.count("C") > 0) return;
computed.insert("C");
std::cout << "c: " << c << std::endl;
}
};
struct D : public B,public C {
int d;
D(int a_,int b_,int c_,int d_) : A(a_), B(a_,b_), C(a_,c_), d(d_) {}
virtual void print_(std::set <std::string>& computed) {
B::print_(computed);
C::print_(computed);
if(computed.count("D") > 0) return;
computed.insert("D");
std::cout << "d: " << d << std::endl;
}
};
int main() {
D d(1,2,3,4);
d.print();
}
在任何情況下,爲解決我會標示出這個問題了。雖然,我一直想聽到更多的答案。
__dummy是保留名稱,您不能使用它 –