微软的Playful Programming??

这几天在刷PSV奖杯,结果看到这么个东西:微软的Visual Studio Achievements扩展

作用就是在程序员完成一些“成就”后给予提示和记录,获取相应的称号。例如:

  • 在一个类里面声明100个成员变量(Field Master)
  • 一行代码写300个字符(Scroll Bar Wizard)
  • 使用“Close All But This”10次(Obsessive Compulsive Disorder)
  • ……

看来以后可以加班刷成就啦!

Objective-C学习笔记(1-3)

去年因为项目需要临时学了一段时间Objective-C,可惜直到现在都适应不了这门语言。学习过程中根据不同主题记了一些东西下来,发出来共享,但愿能帮到一些同为入门菜鸟的Objective-C学习者,不过笔记中肯定有错,这个我比较有自知之明。笔记中的所有时间表达(昨天、这段时间)都不以现在为准,因为是好几个月之前写下的。

目录

  1. 构造和拷贝构造
  2. 动态特性和RTTI
  3. 内存管理和标准C区别

01构造和拷贝构造

首先是有关构造函数

我想实现子类基类不同数量参数的构造方式,结果昨天试验了几种方法都不行。

参见下面的代码,Person是基类,实现了一个两参数的构造函数initWithArgs。子类Coder重载了这个方法,多了一个参数,试图重用基类的方法:

// person的构造函数
- (id) initWithArgs : (NSString*) aName andAge:(int) aAge {
Person* person = [[Person alloc] init];
[person setName: aName];
[person setAge: aAge];
return person;
}
// coder的构造函数
- (id) initWithArgs : (NSString*) aName andAge:(NSInteger) aAge andLanguage:(NSString*)aLanguage {
return ???  //这里要返回什么
}

结果昨天尝试了N种方法,全都不行!

// 不行, crash!
if ( self = [super initWithArgs:aName andAge:aAge] ) {
[self setLanguage:aLanguage];
}
return self;
 
// 不行,没有crash,但是基类的参数name和age都设置失败
[super initWithArgs:aName andAge:aAge];
[self setLanguage:aLanguage];
return self;
 
// 好用,但我们的基类构造函数不就没用了吗
Coder* coder = [[Coder alloc] init];
[coder setName:aName];
[coder setAge:aAge];
[coder setLanguage:aLanguage];
return coder;

问题在于基类的构造定义,要用[self class]获取到当前self指针的类型才对

Person* person = [[[self class] alloc] init];

子类的构造函数:

Coder* coder = [super initWithArgs:aName andAge:aAge];
[coder setLanguage:aLanguage];
return coder;

或者用更单纯一点的调用:

[self initWithArgs:aName andAge:aAge];
    [self setLanguage:aLanguage];
    return self;

Read more >>>

《深度探索C++对象模型》学习笔记(5-7章)

第5章 构造析构和拷贝

虚函数

对于抽象类而言,是否应该自己负责初始化类内的数据成员?(别忘了抽象类也可以有非纯虚函数和数据成员)

  • 一般情况下被认为子类要负责从基类继承来的数据成员的初始化
  • 或者基类必须提供一个显式的构造函数用于自己数据成员的初始化
  • 最好的方法则是将行为和数据分离,提供接口专门用于定义方法
class A_B
{
public:
    virtual ~A_B() = 0;
    virtual void Say() const = 0;
    inline char const* GetData() const { return iData; }
protected:
    A_B( char* aData ) : iData( aData ) {  }
protected:
    char* iData;
};
inline A_B::~A_B() {}
 
class C_D : public A_B // concrete derived class
{
public:
    C_D( char* aData, char* aData2 ) :
           A_B( aData ), iData2( aData2 ) { cout< <"c_d ctor"<<endl; }
    virtual ~C_D() { delete iData2; }
    virtual void Say() const { cout<<"c_d says: "<<iData<<endl; }
    void Say2() const { cout<<"c_d says2: "<<iData2<<endl; }
public:
    char* iData2;
};

纯虚函数

  • 拥有定义的纯虚函数(这还叫纯虚函数么)可以在子类中进行调用。不过就是什么用都没有罢了。
//定义
inline void A_B::Say() const { cout< <"hey!"; }
//调用,发现hey!并没有打印出来
virtual void Say() const { A_B::Say(); cout<<"c_d says: "<<iData<<endl; }
  • 实际上是否让纯虚函数拥有(没用的)定义由程序员说了算。
  • 但唯一的例外是纯虚析构函数,必须像上面那样进行显式定义 A_B::~A_B() {}
    • 否则就会出现错误,严格来说并非编译错误,而是链接错误。
    • 原因很简单,析构函数和构造函数一样,子类的都会由编译器进行扩充,调用基类的版本。
    • 不希望这么定义的话,解决方法只有生命非纯虚的析构函数

Virtual Specification

  • 不加选择地将函数设定为virtual在效率上会不升反降,不能依赖编译器的优化
  • 应付const也会令人头疼,例如在基类中不需要修改的const ref或者const pointer,到了子类就需要修改了。目前最好的方法是不用const(……无语)
  • 需要考虑到的(不全)
    • 抽象类的构造函数需要负责初始化自己的数据成员,可以声明为protected避免外部访问
    • 缺少多态需求的函数不要设定为virtual,尤其是inline函数
    • 慎用const,除非确定派生类也不会修改const修饰的对象

Read more >>>

打自己脸——有关.net对象分配

之前发过的那篇.NET的GC机制和GC知识学习里面有一选项为:

A. 值类型的对象会被分配到Stack上,而引用类型的对象会被分配到Large Object Heap上。

今天在写前一篇Blog时候,发现这句话其实大错特错,虽然这个选项本身就是故意加入了错误,作为选择题答案出现,但此错非彼错。还好题目没有正式使用,不然会被人打脸打得啪啪响。在尚未被人打脸之前还是先自打比较好。先看前半句:

值类型的对象会被分配到Stack上

拿值类型中最常被提到用来和引用类型相比较的Struct来说,它一定分配到Stack上吗?当然不是。这一点题目已经涉及到了,就是大于85k的对象会分配到LOH上。但忽略的一点是一般尺寸的值类型对象也有可能分配到Heap上:

  • 类成员变量,就算是值类型也是在Heap上分配;
  • 局部值类型变量,涉及到和闭包相关的特性时。

第一条就不说了。第二条例如下面这个例子,i和j都是局部值类型变量,他们都在Stack上吗?

private void Form1_Load( object sender, EventArgs e ) {
    int j = 0;
    int i = 0;
    Func foo = () =&gt; {
        return i;
    };
}
当然是否定的,拿出il代码看一下就明了:从声明上来看,j的确是在Stack上没错,但i却是依附在一个编译器自动生成的类<>c__DisplayClass3上面;进行赋值时候i用的是stfld而不是stloc。这也就是说对于闭包涉及到的局部变量而言,CLR会将其依附到一个自动生成的类上进行存取操作,那么它自然就不分配到Stack上了。
.method private hidebysig instance void  Form1_Load(object sender,
class [mscorlib]System.EventArgs e) cil managed
{
// 代码大小       31 (0x1f)
.maxstack  3
.locals init (<span style="color: #ff0000;">[0] int32 j</span>,
[1] class [System.Core]System.Func`1 foo,
<span style="color: #ff0000;">[2] class WindowsFormsApplication1.Form1/'&lt;&gt;c__DisplayClass3' 'CS$&lt;&gt;8__locals4'</span>)
IL_0000:  newobj     instance void WindowsFormsApplication1.Form1/'&lt;&gt;c__DisplayClass3'::.ctor()
IL_0005:  stloc.2
IL_0006:  nop
IL_0007:  ldc.i4.0
IL_0008:  <span style="color: #ff0000;">stloc.0</span>
IL_0009:  ldloc.2
IL_000a:  ldc.i4.0
IL_000b:  <span style="color: #ff0000;">stfld      int32 WindowsFormsApplication1.Form1/'&lt;&gt;c__DisplayClass3'::i</span>
IL_0010:  ldloc.2
IL_0011:  ldftn      instance int32 WindowsFormsApplication1.Form1/'&lt;&gt;c__DisplayClass3'::'
b__2'()
IL_0017:  newobj     instance void class [System.Core]System.Func`1::.ctor(object,
native int)
IL_001c:  stloc.1
IL_001d:  nop
IL_001e:  ret
} // end of method Form1::Form1_Load

另外漏了一点,在更正:值类型并不总是分配在栈上一文中提到,还有一种会被分配到Heap上的值类型:

  • 数组内的值类型对象,会被一同分配到Heap上。

一些关于闭包的废话

继函数形参传值传指针、GC机制的Blog后,貌似这又是说烂的问题?没办法,最近在回顾各种知识,发现有联系的就拿出来写一写,省得日后发霉。如果你现在对闭包还不是很了解,可以阅读一下学习Javascript闭包(Closure)深入理解JavaScript闭包(1)理解 JavaScript 闭包三篇文章。难度依次上升。

JavaScript的闭包

闭包的概念已经比科学发展观都深入人心了,简单说它就是“具有独立作用域并且可以引用外层作用域的对象”。例如C#中的匿名函数和Lambda表达式就是基于的闭包概念,其他的高级语言,类似Java、JavaScript和Python等也都有闭包的应用。当然也不要忘了JavaScript的内存泄露主因之一也是闭包,所以先看JS好了。

还是先从代码入手,下面两段代码是从阮一峰老师的那篇闭包讲解中摘取的最后习题,那么现在提问,两段代码分别alert什么内容?

var bar = "Global";
  var object1 = {
    bar : "local",
    foo : function(){
      return function(){
        return this.bar;
      };
    }
  };
  alert(object1.foo()());
 
//我是分隔线
 
  var object2 = {
    bar : "local",
    foo : function(){
          var that = this;
      return function(){
        return that.bar;
      };
    }
  };
  alert(object2.foo()());

Read more >>>