Wednesday, January 19, 2011

昨天又一个电话面试失败了

到现在为止,第二个电话面试,也失败了。

原因估计是他们需要low level的embedded developer,似乎还要和kernel有些关联。当然,我以前在施乐的工作和kernel是没什么关系的。雇主也就兴趣缺缺了。过程中,雇主还提到他们做的是微型仪器,硬件限制很大,比如ram,和flash memory方面的限制似乎cap在640KB。他还问到我国内的专业对应于美国的什么专业,我跟他说对应于美国的Mechanical Engineering,他还问我有没有CS方面的学位?我说没有,全都是learn on the job and learn by myself and by google,似乎他不是很满意。

也无所谓了。

Friday, January 14, 2011

今天路考通过,拿到驾照。

考完后,我还忐忑不安地坐在桌前,看她是不是准备再抽出一张Evaluation Report,然后数落我的缺点,没想到她敲了一会儿计算机之后,就对我说“Eight Dollars”,也就说我过了,她这是问我要办驾照的钱。

欣喜若狂。

坚韧和努力才是通向成功的唯一道路。

Monday, January 10, 2011

Virtual functions in C++

昨天读到《Accelerated C++》的第13章Using inheritance and dynamic binding,才对virtual function有了正确的认识。

原文如下:
---
13.2 Polymorphism and virtual functions


We have not yet completely reimplemented the original Student_info abstraction. That abstraction relied on a nonmember function to support part of its interface: It used the compare function to compare two student records. This function is used by sort to arrange records in alphabetical order.

Our new comparison function is identical to the one we wrote in §9.3.1/162 except for the change in type name:

bool compare(const Core& c1, const Core& c2)
{
    return c1.name() < c2.name();
}

We compare two student records by comparing their names. We delegate the real work to the string library < operator. What is interesting about this code is that we can use it to compare both Core records and Grad records, or even to compare a Core record with a Grad record:

Grad g(cin); // read a Grad record
Grad g2(cin); // read a Grad record
Core c(cin); // read a Core record
Core c2(cin); // read a Core record

compare(g, g2); // compare two Grad records
compare(c, c2); // compare two Core records
compare(g, c); // compare Grad record with a Core record

In each of these calls to compare, the name member of class Core will be run to determine the value to return from compare. Obviously, this is the right member to call for class Core, but what about for Grad? When we defined class Grad, we said that it is inherited from Core, and we did not redefine the name function. Thus, when we invoke g.name() for a Grad object g, we are invoking the name member that it inherited from Core. That function operates the same way on a Grad as it does on a Core: It fetches the underlying n field from the Core part of the object.

The reason that we can pass a Grad object to a function expecting a Core& is that we said that Grad is inherited from Core, so every Grad object has a Core part:


Because every Grad object has a Core part, we can bind compare's reference parameters to the Core portions of Grad objects, exactly as we can bind them to plain Core objects. Similarly, we could have defined compare to operate on pointers to Core or on objects of type Core (as opposed to a reference to Core). In either case, we could still call the function on behalf of a Grad object. If the function took pointers, we could pass a pointer to Grad. The compiler would convert the Grad* to a Core*, and would bind the pointer to the Core part of the Grad object. If the function took a Core object, then what would be passed is just the Core portion of the object. There can be striking differences in behavior, depending on whether we pass an object itself, or a reference or pointer to the object—as we shall now see.

13.2.1 Obtaining a value without knowing the object's type

Our compare function does the right thing when we call it with a Grad object as an argument because the name function is shared by both Grad and Core objects. What if we wanted to compare students, not on the basis of their names, but on the basis of their final grades? For example, instead of producing a listing of final grades sorted by name, we might need to produce a listing sorted by final grade.

As a first cut at solving this problem, we'd write a function that is similar to compare:

bool compare_grades(const Core& c1, const Core& c2)
{
    return c1.grade() < c2.grade();
}

The only difference is that here we're invoking the grade function rather than the name function. This difference turns out to be significant!

The difference is that Grad redefines the meaning of the grade function, and we have done nothing to distinguish between these two versions of grade. When we execute the compare_grades function, it will execute the Core::grade member, just as compare executes Core::name. In this case, if we are operating on a Grad object, then the version from Core gives the wrong answer, because the grade functions in Core and Grad behave differently from each other. For Grad objects, we must run Grad::grade in order to account for the thesis.

What we need is a way for compare_grades to invoke the right grade function, depending on the actual type of object that we pass: If c1 or c2 refers to a Grad object, then we want the Grad version of grade; if the object is of type Core, then we want the one from Core. We want to make that decision at run time. That is, we want the system to run the right function based on the actual type of the objects passed to the function, which is known only at run time.

To support this kind of run-time selection, C++ provides virtual functions:

class Core {
public:
    virtual double grade() const; // virtual added
    // ...
};

We now say that grade is a virtual function. When we call compare_grades, the implementation will determine the version of grade to execute by looking at the actual types of the objects to which the references c1 and c2 are bound. That is, it will determine which function to run by inspecting each object that we passed as an argument to compare_grades. If the argument is a Grad object, it will run the Grad::grade function; if the argument is a Core object, it will run the Core::grade function.

The virtual keyword may be used only inside the class definition. If the functions are defined separately from their declarations, we do not repeat virtual in the definitions. Thus, the definition of Core::grade() need not change. Similarly, the fact that a function is virtual is inherited, so we need not repeat the virtual designation on the declaration of grade within the Grad class. We do have to recompile our code with the new Core class definition. Once we have done so, then because the base-class version is virtual, we get the behavior that we need.

13.2.2 Dynamic binding

This run-time selection of the virtual function to execute is relevant only when the function is called through a reference or a pointer. If we call a virtual function on behalf of an object (as opposed to through a reference or pointer), then we know the exact type of the object at compile time. The type of an object is fixed: It is what it is, and does not vary at run time. In contrast, a reference or pointer to a base-class object may refer or point to a base-class object, or to an object of a type derived from the base class, meaning that the type of the reference or pointer and the type of the object to which a reference or pointer is bound may differ at run time. It is in this case that the virtual mechanism makes a difference.

For example, assume we rewrote compare_grades as follows:

// incorrect implementation!
bool compare_grades(Core c1, Core c2)
{
    return c1.grade() < c2.grade();
}

In this version, we say that our parameters are objects, not references to objects. In this case, we always know the type of objects represented by c1 and c2: They are Core objects. We can still call this function on behalf of a Grad object, but the fact that the argument had type Grad is immaterial. In this case, what happens is that what we pass is the base part of the object. The Grad object will be cut down to its Core part, and a copy of that portion of the Grad object will be passed to the compare_grades function. Because we said that the parameters are Core objects, the calls to grade are statically bound—they are bound at compile time—to Core::grade.

This distinction between dynamic binding and static binding is essential to understanding how C++ supports OOP. The phrase dynamic binding captures the notion that functions may be bound at run time, as opposed to static bindings that happen at compile time. If we call a virtual function on behalf of an object, the call is statically bound— that is, it is bound at compile time—because there is no possibility that the object will have a different type during execution than it does during compilation. In contrast, if we call a virtual function through a pointer or a reference, then the function is dynamically bound—that is, bound at run time. At run time, the version of the virtual function to use will depend on the type of the object to which the reference or pointer is bound:


Core c;
Grad g;
Core* p;
Core& r = g;

c.grade(); // statically bound to Core::grade()
g.grade(); // statically bound to Grad::grade()
p->grade(); // dynamically bound, depending on the type of the object to which p points
r.grade(); // dynamically bound, depending on the type of the object to which r refers

The first two calls can be statically bound: We know that c is a Core object, and that at run time, c will still be a Core object. Therefore, the compiler can statically resolve this call, even though grade is a virtual function. In the third and fourth calls, however, we can't know the type of the object to which p or r refers until run time: They might be Core or Grad objects. Hence, the decision as to which function to run in these cases must be delayed until run time. The implementation makes that decision based on the type of the object to which p points or to which r refers.

The fact that we can use a derived type where a pointer or reference to the base is expected is an example of a key concept in OOP called polymorphism. This word, from the Greek polymorphos, meaning "of many forms," was already in use in English in the mid-nineteenth century. In a programming context, it refers to the ability of one type to stand in for many types. C++ supports polymorphism through the dynamic-binding properties of virtual functions. When we call a virtual through a pointer or reference, we make a polymorphic call. The type of the reference (or pointer) is fixed, but the type of the object to which it refers (or points) can be the type of the reference (or pointer) or any type derived from it. Thus, we can potentially call one of many functions through a single type.

One final note about virtual functions: These functions must be defined, regardless of whether the program calls them. Nonvirtual functions may be declared but not defined, as long as the program does not call them. Many compilers generate mysterious error messages for classes that fail to define one or more virtual functions. If your program evokes a message from the compiler that you do not understand, and that message says that something is undefined, you should verify that you have defined all of your virtual functions. You are likely to find that the error goes away when you do so.

13.2.3 Recap

Before we continue, it is probably worth summarizing where we are, and making one slight additional change: We'll make the read function virtual as well. We'd like to be able to have the choice of which read function to run depend on the type of the object on which it is invoked. With that final change, let's look at our classes:

class Core {
public:
    Core(): midterm(O), final (0) { }
    Core(std::istream& is) { read(is); }
    std::string name() const;


    // as defined in §13.1.2/230
    virtual std::istream& read(std::istream&);
    virtual double grade() const;

protected:
    // accessible to derived classes
    std::istream& read_common(std::istream&);
    double midterm, final;
    std::vector homework;

private:
    // accessible only to Core
    std::string n;
};

class Grad: public Core {
public:
    Grad(): thesis(0) { }
    Grad(std::istream& is) { read(is); }

    // as defined in §13.1.2/230; Note: grade and read are virtual by inheritance
    double grade() const;
    std::istream& read(std::istream&);

private:
    double thesis;
};

bool compare(const Core&, const Core&);

We have defined two classes to encapsulate our two kinds of students. The first class, Core, represents students meeting the core requirements for the course. Our second class inherits from Core, adding the requirements for completing a thesis. We can create Core or Grad objects in two ways. The default constructor creates a properly initialized, empty object; the other constructor takes an istream& and reads initial values from the specified stream. The operations let us read into an object, resetting its values, and let us fetch the student's name or final grade. Note that in this version, we have made both the grade and read functions virtual. Finally, our interface includes a global, nonmember compare function that compares two objects by comparing students' names.
---
也就是说,virtual functions的作用是为了dynamic binding/polymorphism之用的。


对比之下,overload则是另外一种概念。
---
Functions: A function must be declared in every source file that uses it, and defined only once. The declarations and definitions have similar forms:


ret-typefunction-name (parm-decls) // function declaration
[inline] ret-typefunction-name (parm-decls) { // function definition
    // function body goes here
}

Here, ret-type is the type that the function returns, parm-decls is a comma-separated list of the types for the parameters of the function. Functions must be declared before they are called. Each argument's type must be compatible with the corresponding parameter. A different syntax is necessary to declare or define functions with sufficiently complicated return types; see §A.1.2/297 for the full story.

Function names may be overloaded: The same function-name may define multiple functions so long as the functions differ in the number or types of the parameters. The implementation can distinguish between a reference and a const reference to the same type.

We can optionally qualify a function definition with inline, which asks the compiler to expand calls to the function inline when appropriate—that is, to avoid function-call overhead by replacing each call to the function by a copy of the function body, modified as necessary. To do so, the compiler needs to be able to see the function definition, so inlines are usually defined in header files, rather than in source files.
---
简言之,overload是一种根据不同的形参调用不同函数的编译机制。

从广义的概念上讲,可能dynamic binding也可以算作某种overload。但严格来说,二者的应用范围还是不太一样的。前者和C++的核心-Class-紧密相关,而后者与Class是没有关系的。

由此可以想见,那次电话面试的第一题我也没有答对。

又查了一下书,dynamic binding对应的还是不能算overload,连广义的都不能算:
---
Dynamic binding refers to the ability to select at run time which function to run based on the actual type of the object on which the function is called. Dynamic binding is in effect for calls to virtual functions made through a pointer or a reference. The fact that a function is virtual is inherited, and need not be repeated in the derived classes.


Derived classes are not required to redefine their virtual functions. If a class does not redefine a virtual, then it inherits the nearest definition for that function. However, any virtual functions that the class does contain must be defined. It is often a source of mysterious error messages from compilers to declare but not define a virtual function.

Overriding: A derived-class member function overrides a function with the same name in the base class if the two functions have the same number and types of parameters and both (or neither) are const. In that case, the return types must also match, except that as in §13.4.2/246, if the base-class function returns a pointer (or reference) to a class, the derived-class function can return a pointer (or reference) to a derived class. If the argument lists don't match, the base- and derived-class functions are effectively unrelated.
---
由此可见,dynamic binding对应的是override,而不是overload。

Monday, January 3, 2011

今天早上的路考又一次失败了

已经是第三次了,真是沮丧。

考官提的要点:
1. Quick Stop - Did not complete. 我倒是听到考官说的pull up的命令,但我以为pull up的意思是慢慢、稳当地停在路边,而不是急刹车。查了一下m-w.com,看来是我错了(to come to an often abrupt halt )。
2. Backing - Did not follow instructions. 这是因为我没有听清楚,当时应该再问他一下的。
3. Right/Left Turns - Has limited control of vehicle. 比前两次的no control要好些,但非常有限。
4. Use of Lanes - Crossed center line/Move left of center. 这是在我刚从停车场出去的时候,没有拐好导致的。不过地上两条lane中间的白线根本看不到,都被磨没了。而且以前的两次考官没有提到过这一点。


还要继续练习啊,自勉一下:Whatever didn't kill you makes you stronger.