By default, derived classes inherit all of the behaviors defined in a base class. In this lesson, we’ll examine in more detail how member functions are selected, as well as how we can leverage this to change behaviors in a derived class.
默认地,派生类继承基类的所有行为(在继承类型的限定下)。在本节,我们会尝试更多的“关于怎么选择一个基类中的成员函数,我们怎么在派生类中改变基类中的成员函数”的问题。
When a member function is called with a derived class object, the compiler first looks to see if that member exists in the derived class. If not, it begins walking up the inheritance chain and checking whether the member has been defined in any of the parent classes. It uses the first one it finds.
当派生类对象的成员函数被调用时,编译器首先会查看这个函数在不在派生类里面,如果不在的话,它会沿着继承链去找这个函数在不在基类当中。它会使用第一个它找到的函数。
Consequently, take a look at the following example:
class Base
{
protected:
int m_value;
public:
Base(int value)
: m_value(value)
{
}
void identify() { std::cout << "I am a Base\n"; }
};
class Derived: public Base
{
public:
Derived(int value)
: Base(value)
{
}
};
int main()
{
Base base(5);
base.identify();
Derived derived(7);
derived.identify();
return 0;
}
This prints
I am a Base
I am a Base
When derived.identify() is called, the compiler looks to see if function identify() has been defined in the Derived class. It hasn’t. Then it starts looking in the inherited classes (which in this case is Base). Base has defined an identify() function, so it uses that one. In other words, Base::identify() was used because Derived::identify() doesn’t exist.
在identify函数被调用的时候,编译器会查看一下identify是否在派生类中被定义,如没有的话,它会去基类中查找,因为base中有这个函数,所以他就用了那个函数。换句话说,如果派生类中没有的话,它就使用基类中的函数。
This means that if the behavior provided by a base class is sufficient, we can simply use the base class behavior.
这意味着,如果基类中的函数足够用的话,我们就可以直接使用基类的函数行为,不用在派生类中实现。
Redefining behaviors
However, if we had defined Derived::identify() in the Derived class, it would have been used instead.
然而,如果我们定义一个派生类的identify函数在派生类中。它就会被编译器使用。
This means that we can make functions work differently with our derived classes by redefining them in the derived class!
这意味我们可以通过在派生类中再定义一个函数,让我们的函数行为在派生类中和基类中的表现不一样。
In our above example, it would be more accurate if derived.identify()
printed “I am a Derived”. Let’s modify function identify() in the Derived class so it returns the correct response when we call function identify() with a Derived object.
在上面的例子中,如果派生类的identify函数打印说我是派生类,会更准确一些。让我们在派生类中修改identfy函数,让他的回应更合理一些。
To modify the way a function defined in a base class works in the derived class, simply redefine the function in the derived class.
要改变identify在派生类对象上的行为,只需要在派生类中重新定义一下identify函数就可以。
class Derived: public Base
{
public:
Derived(int value)
: Base(value)
{
}
int getValue() { return m_value; }
// Here's our modified function
void identify() { std::cout << "I am a Derived\n"; }
};
Here’s the same example as above, using the new Derived::Identify() function:
int main()
{
Base base(5);
base.identify();
Derived derived(7);
derived.identify();
return 0;
}
I am a Base
I am a Derived
Note that when you redefine a function in the derived class, the derived function does not inherit the access specifier of the function with the same name in the base class. It uses whatever access specifier it is defined under in the derived class. Therefore, a function that is defined as private in the base class can be redefined as public in the derived class, or vice-versa!
注意当你在派生类中重新定义函数的时候,派生类中的同名函数不会继承基类中的访问限定符。它会使用在派生类中设置的访问限定符,因此,一个函数在基类中被定义为private,可以在派生类中定义为public,或者反过来,在基类中是public,在派生类中是private。
class Base
{
private:
void print()
{
std::cout << "Base";
}
};
class Derived : public Base
{
public:
void print()
{
std::cout << "Derived ";
}
};
int main()
{
Derived derived;
derived.print(); // calls derived::print(), which is public
return 0;
}
Sometimes we don’t want to completely replace a base class function, but instead want to add additional functionality to it. In the above example, note that Derived::identify() completely hides Base::identify()! This may not be what we want. It is possible to have our derived function call the base version of the function of the same name (in order to reuse code) and then add additional functionality to it.
在某些时候我们不想完全的替换掉基类中的函数,但是想给他增加额外功能。在上面的例子中。注意派生的identify完全覆盖了identify。这可能不是我们想要的。但是让我们的派生类调用基类中的同名的函数是可以的(为了代码重用性)。然后添加一些额外的功能。
To have a derived function call a base function of the same name, simply do a normal function call, but prefix the function with the scope qualifier (the name of the base class and two colons). The following example redefines Derived::identify() so it first calls Base::identify() and then does its own additional stuff.
为了让派生类函数调用基类函数,简单的只需要做一个简单的函数调用。但是需要给这个函数加类名限定。下面的函数先调用了基类中的版本,然后才开始做自己的事情。
class Derived: public Base
{
public:
Derived(int value)
: Base(value)
{
}
int GetValue() { return m_value; }
void identify()
{
Base::identify(); // call Base::identify() first
std::cout << "I am a Derived\n"; // then identify ourselves
}
};
Now consider the following example:
int main()
{
Base base(5);
base.identify();
Derived derived(7);
derived.identify();
return 0;
}
I am a Base
I am a Base
I am a Derived
When derived.identify()
is executed, it resolves to Derived::identify(). However, the first thing Derived::identify() does is call Base::identify(), which prints “I am a Base”. When Base::identify() returns, Derived::identify() continues executing and prints “I am a Derived”.
当这一行代码被执行的时候,它首先调用了派生类的identify函数。但,在派生类identify函数体中做的第一件事就是调用基类的identify函数,当基类identify返回的时候,派生类继续执行。
This should be pretty straightforward. Why do we need to use the scope resolution operator (::)? If we had defined Derived::identify() like this:
这个应该是非常直接的。但是为什么我们要使用这个类名限定呢?
class Derived: public Base
{
public:
Derived(int value)
: Base(value)
{
}
int GetValue() { return m_value; }
void identify()
{
identify(); // Note: no scope resolution!
cout << "I am a Derived";
}
};
Calling function identify() without a scope resolution qualifier would default to the identify() in the current class, which would be Derived::identify(). This would cause Derived::identify() to call itself, which would lead to an infinite loop! 调用identify不加类名限定的话,就会调用派生类的identify函数。这会导致identify自己调用自己,产生一个无限循环。
There’s one bit of trickiness that we can run into when trying to call friend functions in base classes, such as operator<<. Because friend functions of the base class aren’t actually part of the base class, using the scope resolution qualifier won’t work. Instead, we need a way to make our Derived class temporarily look like the Base class so that the right version of the function can be called.
有一个小技巧,我当我们试图调用基类中的友元函数的时候,例如流输出运算符。因为友元函数并不是基类的一部分,使用类名限定是不正确的。所以我们要有一种方式能让我们的派生类临时的看起来像基类,以至于正确的函数版本可以被使用。
Fortunately, that’s easy to do, using static_cast. Here’s an example:
幸运的是只要使用static_cast就可以了。
class Base
{
private:
int m_value;
public:
Base(int value)
: m_value(value)
{
}
friend std::ostream& operator<< (std::ostream &out, const Base &b)
{
out << "In Base\n";
out << b.m_value << '\n';
return out;
}
};
class Derived : public Base
{
public:
Derived(int value)
: Base(value)
{
}
friend std::ostream& operator<< (std::ostream &out, const Derived &d)
{
out << "In Derived\n";
// static_cast Derived to a Base object, so we call the right version of operator<<
out << static_cast<Base>(d);
return out;
}
};
int main()
{
Derived derived(7);
std::cout << derived;
return 0;
}
Because a Derived is-a Base, we can static_cast our Derived object into a Base, so that the appropriate version of operator<< that uses a Base is called.
因为派生类对象是一个基类对象,所以我们可以用static_cast来把我们的派生类对象转换(投影)成基类对象,然后合适的重载函数版本就会被调用。。
This prints:
In derived
In base
7