Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Adaptations


November, 2004: Adaptations

Herb Sutter (http://www.gotw.ca/) is a leading authority and trainer on C++ software development. He chairs the ISO C++ Standards committee and is a Visual C++ architect for Microsoft, where he is responsible for leading the design of C++ language extensions for .NET programming. His two most recent books are Exceptional C++ Style and C++ Coding Standards (forthcoming October 2004). Jim Hyslop is a senior software designer for Leitch Technology International. He can be reached at jhyslop@ ieee.org.


Let me give you a word of advice, pardner," Wendy's voice floated over the cubicle wall.

"Hey, welcome back," I called as I gophered up. "How's things in the UK office?"

"The usual," Wendy waved me off. "As I was saying though, my advice is: Never trust a home-built AC voltage adaptor." I cocked an eyebrow quizzically. "You know that in the UK everything runs on 230 volts, right? Well, Tom thought he'd save a bit of money and build a voltage converter for my hair dryer, rather than buying one. Only one problem—he wired it up backwards, and I fried my hair dryer the instant I turned it on. Poof!"

"Aw, that's too bad," I commiserated. "I'm glad you're back, though. I've been wrestling with something I think you can help me with. Come on over after you're settled in."

A short while later, Wendy joined me in my cubicle.

"The problem is," I said as I called up the code I had been working on, "I'm deriving my class from two different functions, each of which has a virtual function with the same name. I've distilled it down to this."

class Base1
{
public:
  virtual void VirtFunc()
  {
    cout << "Base1::VirtFunc" << endl;
  }
};
class Base2
{
public:
  virtual void VirtFunc()
  {
    cout << "Base2::VirtFunc" << endl;
  }
};

class D : public Base1, public Base2
{
public:
  virtual void VirtFunc()
  {
    cout << "D::VirtFunc" << endl;
    Base1::VirtFunc();
    Base2::VirtFunc();
  }
};
void f( Base1 & b );
void g( Base2 & b );

"When I call f, I need it to call Base1::VirtFunc, and when I call g, I need it to call Base2::VirtFunc. But, I can't find a way to override both versions of the function."

"Why not rename one or both functions in the base class so they more accurately reflect what they do? You know, good naming conventions, and all..."

"All the Base1 stuff is in a different library from all the Base2 stuff. Third-party libraries. And besides, even a good name can clash—like Draw or Write."

"Yeah, okay. Well, I think we already talked about your solution, pardner." Wendy smirked at me.

"We have?" I scratched my head.

"Sure—only I hope your adaptor won't zap things like mine did," she said.

"Adaptor? Zap?!? Oh, I get it—create an intermediate class to act as an adaptor. Are you sure?" I didn't wait for an answer, but turned back to the keyboard and started typing:

class Base1Intermediate : public Base1
{
public:
  void VirtFunc()
  {
    cout << 
      "Base1Intermediate::VirtFunc"
      << endl;
  }
};

class Base2Intermediate : public Base2
{
public:
  void VirtFunc()
  {
    cout << 
      "Base2Intermediate::VirtFunc" 
      << endl;
  }
};

"But how do I override VirtFunc in the main class—D?"

"You don't," Wendy said as she took the keyboard from me. "Just to show the difference, I'm going to create a new class—D2."

class D2 : 
  public Base1Intermediate, 
  public Base2Intermediate
{
};

"Notice I didn't override VirtFunc—that's the key. When you pass the D2 to f, the final overrider for it is Base1Intermediate's VirtFunc. Same idea for g." She then added some more implementations:

void f( Base1 & b )
{
  cout << "In function f()\n";
  b.VirtFunc();
  cout << "\n";
}

void g( Base2 & b )
{
  cout << "In function g()\n";
  b.VirtFunc();
  cout << "\n";
}
int main()
{
  D d;
  cout << "In main, with d\n";
  f( d );
  g( d );

  D2 d2;
  cout << "In main, with d2\n";
  f( d2 );
  g( d2 );
  return 0;
}

I looked it over, and then we compiled and ran the program:

In main, with d
In function f()
D::VirtFunc
Base1::VirtFunc
Base2::VirtFunc

In function g()
D::VirtFunc
Base1::VirtFunc
Base2::VirtFunc

In main, with d2
In function f()
Base1Intermediate::VirtFunc

In function g()
Base2Intermediate::VirtFunc

"Hey, that's pretty cool," I said. "But what if we also wanted to have an override that calls both functions?" We jumped at the customary snap that I immediately recognized as the Guru's.

"Then decide which personality you want to present, you must," the Guru said softly. "Consider, my child, what you have already bespoken. When in the context of a Base1, your class must act like a Base1. When in the context of a Base2, it must act like a Base2. When in the context of its own, then it must find its own place by presenting its own function, which then calls the two virtual functions." She picked up a marker and wrote on the whiteboard:

void D2::NonVirtfunc()
  {
    Base1Intermediate::VirtFunc();
    Base2Intermediate::VirtFunc();
  }

"The function may, of course, be virtual if necessary."

"So, now that you're here," I grinned mischievously, "is there any way to avoid the intermediate classes?"

"Not really, my child," the Guru shook her head. "Although it has been suggested that an extension to the language could permit it thusly:"

class Fantasy : public Base1, public Base2
{
public:
  void VirtFunc1() overriding Base1::VirtFunc;
  void VirtFunc2() overriding Base2::VirtFunc;
};

"As the syntax suggests—and I must emphasize, my children, that this is pure fantasy and is not, to my knowledge, being considered by the disciples on the ISO C++ Standards committee—VirtFunc1 would override Base1's function, and be used in the context of Base1. Similarly, VirtFunc2 would override Base2's function, and be used in the context of Base2.

"Until such time as fantasy becomes reality," she said as she turned and glided away, "the intermediate adaptors are a good mechanism for achieving your goal."


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.