放牧代码和思想
专注自然语言处理、机器学习算法
    This thing called love. Know I would've. Thrown it all away. Wouldn't hesitate.

从构造器内部多态看C++的严谨与Java的陷阱

在 Think in Java 多态这章中为了讲解构造器多态的层次结构,举出了一个有意思的例子。我将这个Java例子“翻译”为C++,果然得到了不同的结果。从这种结果来看,C++比Java更加“严谨”。先来看看Bruce Eckel给出的例子:

package com.hankcs.polymorphism;//: polymorphism/PolyConstructors.java
// Constructors and polymorphism
// don't produce what you might expect.

import static net.mindview.util.Print.*;

class Glyph
{
    void draw()
    {
        print("Glyph.draw()");
    }

    Glyph()
    {
        print("Glyph() before draw()");
        draw();
        print("Glyph() after draw()");
    }
}

class RoundGlyph extends Glyph
{
    private int radius = 1;

    RoundGlyph(int r)
    {
        radius = r;
        print("RoundGlyph.RoundGlyph(), radius = " + radius);
    }

    void draw()
    {
        print("RoundGlyph.draw(), radius = " + radius);
    }
}

public class PolyConstructors
{
    public static void main(String[] args)
    {
        new RoundGlyph(5);
    }
} /* Output:
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
*///:~

原本程序员期望第二行输出

RoundGlyph.draw(), radius = 5

然后却输出了

RoundGlyph.draw(), radius = 0

书里面讲得很清楚,这是由于多态决定的。在Glyph构造的时候,draw()方法被子类覆盖,调用的是RoundGlyph::draw()。这是Java的“陷阱”,当然,这是Java的设计初衷。如果程序员足够警惕,它就是天经地义的了。

但是作者在括号里指出“C++语言会产生更合理的行为”,这不禁激起了我的好奇心,于是将这个示例程序“翻译为”C++语言:

#include <iostream>
using namespace std;

class Glyph
{
	virtual void draw()
	{
		printf("Glyph.draw()\n");
	}
public:
	Glyph()
	{
		printf("Glyph() before draw()\n");
		draw();
		printf("Glyph() after draw()\n");
	}
};

class RoundGlyph: public Glyph
{
private:
	int radius;

public:
	RoundGlyph(int r)
	{
		radius = r;
		printf("RoundGlyph.RoundGlyph(), radius = %d\n" , radius);
	}

	virtual void draw()
	{
		printf("RoundGlyph.draw(), radius = %d\n" , radius);
	}
};

///////////////////////////SubMain//////////////////////////////////
int main(int argc, char *argv[])
{
	Glyph *pG = new RoundGlyph(5);
	// error C2248: “Glyph::draw”: 无法访问 private 成员(在“Glyph”类中声明)
  	// pG->draw();
	delete pG;
	system("pause");
	return 0;
}
///////////////////////////End Sub//////////////////////////////////

输出:

Glyph() before draw()
Glyph.draw()
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5

看来,C++的多态似乎更加合理。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » 从构造器内部多态看C++的严谨与Java的陷阱

评论 1

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #1

    并没有不妥啊,反倒觉得c++的方式不妥,因为父类构造器先于子类构造器运行,这时候radius并未赋值嘛,至于为何父类要调子类的方法,那也是很自然的,我都覆写了,那么不掉覆写之后的调什么,覆写的结果应该就是所有调用的地方都使用覆盖后的版本,即使是父类中的调用,这样才是的覆写真正的做到全面覆盖,相比之下c++那种畏首畏脚的做法才是不自然的,不彻底的面相对象设计思路

    叶扬9年前 (2015-09-19)回复

我的作品

HanLP自然语言处理包《自然语言处理入门》