问题1
在验证构造函数与复制构造函数关系的时候,在g++与c++里有不同的两种表述
g++编译器可以通过这段
#include<iostream>
using namespace std;
class Sample{
public:
    int v;
    Sample() { };
    Sample(int n):v(n) { };
    Sample( Sample & x) { v = 2 + x.v ; }
};
Sample & PrintAndDouble( Sample  o)
{
    cout << o.v <<endl;    o.v = 2 * o.v;   return o;
}
int main(){
    Sample a(5);
    Sample b = a;
    Sample c = PrintAndDouble( b );
    cout <<  c.v << endl;
    Sample d;
    d = a;
    cout << d.v ;
}
注意PrintAndDouble函数返回值是引用,而如果写为Sample  PrintAndDouble , g++编译器会报错
但是在vs2010中,写成上述代码,程序就会输出一个莫名其妙的东西比如
9
1441025
5
而终于发上仔细分析的时候,return o确实是return了一个临时变量
而正确结果应该中间值是20因此就崩了……只有写成非引用的时候,就是Sample PrintAndDouble的时候才会输出结果,然后就悲剧了%

问题2
引用
引用做函数参数时,注意引用的对象一定要是一个有实际地址的值,否则要加const
#include<iostream>
using namespace std;
void f( int & a)
{
    cout<<a<<endl;
}
int main()
{
    f(1);
    return 0;

}
这样是不行的……必须把函数定义写为void f( const int & a),因为a作为别名必须指向有实际内存的变量,而常数1不在内存区中

提示:编程的好习惯是在传值作为函数参数的时候,都写为常引用

问题3
分清复制与赋值以及复制构造函数
赋值是给一个已经在内存中有其空间的变量给一个确定的值,或者至少是给个已经确定的值。比如
int a=5,b;
b=a;
第一句是对变量的定义,第二句是赋值,因为已经有b这个东西了
但是复制时完全不一样的。复制是把原本有重新生成一个副本,里面东西都一样,像
int a=5;
int b=a;
首先由一个a变量,值是5,然后把a复制给b
类里面的复制构造函数当出现复制时才起作用
复制构造函数一般写成两种形式
(1)
class box
{
box(box & a);
}这是一种形式,在使用时,可以这样
box x , y ;
y(x);

另外c++默认在定义时就赋值的写法是复制构造函数,如
box y=x;这个等号编译器不会解释成为赋值,而是自动会认为这是一个复制语句,并执行复制构造函数。

复制构造函数的使用时机:
(1)最基本的情况,就像上述两例中的直接显式的复制语句。
(2)函数形参数为类对象的时候
如:
void fun(box b)
{

}
int main()
{
box a;
fun(a);
return 0;
}
函数在运行的时候,由于我们知道改变形参的值并不会改变函数实参的值,这样程序采用的方法就是把a的值复制给fun函数体之内的临时变量,在fun之内对这个临时变量做操作,于是自然要使用复制构造函数
(3)当函数返回值是类的对象的时候
box fun()
{
box a;
return a;
}
int main()
{
box b;
b=fun();
}
研究这个函数b=fun()是一个赋值语句,而这是把fun函数中定义的a赋值给b,但是box中定义的a是一个临时变量,于是需要先复制一个暂时的全局变量,不妨记为b1,在return的时候,执行复制构造函数box b1=a;实际return回去的是b1;在执行完b=fun()之后再把b1这个临时变量析构
(注意如果这个时候传回去的是引用函数就崩了……比如改为
box & fun()
{
box a;
return a;
}
int main()
{
box b;
b=fun();
}
比较上下两段程序,上面一段成了临时变量,但是这个临时变量在析构之前及时的赋值给了b,之后b值一个实际的有自己值的东西
但是第二段,传回来的是引用,同样生成了b1,但是表现为:
box b1=a;//这与上面相同
b=&b1;//上面直接是b=b1
这样b就相当于b1的一个别名,而b1在执行完这句之后就要被析构掉。这样b也就不知道是什么东西了

注意有一种情况应该排除在外
class Sample{
public:
    int v;
    Sample() { };
    Sample(int n):v(n) { };
    Sample( const Sample & x)
    {
        v = 2 + x.v ;
    }
};
Sample  PrintAndDouble( Sample & o){
    cout << o.v <<endl;    o.v = 2 * o.v;   return o;
}
int main(){
    Sample a(5);
    Sample b = a;//复制
    Sample c = PrintAndDouble( b );//左边又是复制
    cout <<  c.v << endl;
    Sample d;
    d = a;
    cout << d.v ;
    return 0;
}
注意这段程序返回的是o,但是没有问题,因为被来传进来的就是真实的值的引用。但是这种情况下在传递实参的时候形参已经使用了一次复制构造函数,在返回的时候就不会再生成临时变量从而再一次调用复制构造函数了
(另:g++要求好严格,复制构造函数必须写const否则直接报错,让我纠结了半天……)

上面这段要是写为Sample & PrintAndDouble( Sample & o)
意思就是Sample & c = o;
Sample c = PrintAndDouble( b );这样的写法,编译器就不再按函数返回来考虑,直接认为是一个复制,会调用复制构造函数
与Sample c ;
c = PrintAndDouble( b );会有区别

另外的一点问题就是,就是当上述(2),(3)两种情况都满足的时候,解释的不是很清楚。我感觉此处混淆了复制与赋值的区别。应该是函数在写为这个样子的时候,即右值是函数返回值的时候,好像写为复制形式或者赋值形式差别不是很大……
函数是Sample  PrintAndDouble( Sample & o)情况下
Sample c = PrintAndDouble( b );
与Sample c;
c = PrintAndDouble( b );
效果是一样的。当然写成
Sample d;
d = a;差别就非常大了
而且现在我也只能得出这个结论了,下面是分析代码,以供研究
#include<iostream>
using namespace std;

class Sample{
public:
    int v;
    Sample() { };
    Sample(int n):v(n) { };
    Sample( const Sample & x)
    {
        cout<<"before copy constructor "<<x.v<<endl;
        v = 2 + x.v ;
        cout<<"copy constructor "<<v<<endl;
    }
};
Sample  PrintAndDouble( Sample o){
    cout << o.v <<endl;    o.v = 2 * o.v;
            return o;
}
Sample  PrintAndDouble1( Sample & o){
    cout << o.v <<endl;    o.v = 2 * o.v;
            return o;
}
Sample &  PrintAndDouble2( Sample & o){
    cout << o.v <<endl;    o.v = 2 * o.v;
            return o;
}
int main(){
    Sample a(5);
    Sample b = a;//复制
    cout<<b.v<<endl;
    cout<<"------------------------"<<endl;
    Sample c ;
    c=PrintAndDouble(b);
    cout<<c.v<<endl;
    cout<<"b="<<b.v<<endl;
    cout<<"------------------------"<<endl;
    Sample d=PrintAndDouble(b) ;
    cout<<d.v<<endl;
    cout<<"b="<<b.v<<endl;
    cout<<"------------------------"<<endl;
    Sample e;
    e=PrintAndDouble1(b) ;
        cout<<e.v<<endl;
        cout<<"b="<<b.v<<endl;
        cout<<"------------------------"<<endl;
            Sample f=PrintAndDouble1(b) ;
                cout<<f.v<<endl;
                cout<<"b="<<b.v<<endl;
                cout<<"------------------------"<<endl;
                Sample g;
                g=PrintAndDouble1(b) ;
                    cout<<g.v<<endl;
                    cout<<"b="<<b.v<<endl;
                    cout<<"------------------------"<<endl;
                    Sample h=PrintAndDouble1(b) ;
                        cout<<h.v<<endl;
                        cout<<"b="<<b.v<<endl;

    return 0;
}

问题4
析构函数
首先一个类只能有一个析构函数
class Ctest
Ctest array[2];
这样的应该是首先析构数组的最后一个元素(已试验过,代码如下)
#include<iostream>
using namespace std;
class Ctest {
public:
int date;~Ctest() 
{  cout<< "destructor called" << date<<endl; }

};

int main ()
{
    Ctest arr[2];    arr[0].date=0;
arr[1].date=1;
    cout << "End Main" << endl;

}
像vector之类的类数组型容器也是一样的

class CMyclass {
public:
    ~CMyclass()
{ cout << "destructor" << endl; }

};

CMyclass obj;

CMyclass fun(CMyclass sobj )
{ //参数对象消亡也会导致析                    //构函数被调用
    return sobj;   //函数调用返回时生成临时对象返回

}

void main()
{
    obj = fun(obj);  //函数调用的返回值(临时对象)被   
    //用过后,该临时对象析构函数被调用

}
分析这一段代码,更验证了前面的结论,就是把使用复制构造函数的(2)(3)中情况考虑在一起的时候,依然是要生成两次就是在实参传递给形参的时候生成临时变量,使用一次复制构造,当然也使用析构,函数返回时候生成临时变量再次使用复制构造,依然使用析构函数

#include<iostream>
using namespace std;

class Demo {
    int id;
public:
    Demo(int i)
     {
        id = i;
        printf( "id=%d,Construct\n",id);
    }
    ~Demo()
     {
    printf( "id=%d,Destruct\n",id);
    }
};
Demo d1(1);
void fun()
{
    static Demo d2(2);
    Demo d3(3);
    printf( "fun \n");
}
int main ()
{
    Demo d4(4);
    printf( "main \n");
    {
        Demo d5(5);
    }
    fun();
    printf( "endmain \n");
}

上面这段代码有几个迷惑点,首先是Demo d5(5);这个太黑了……注意他用{}扩了起来,那么就意味着在}之后就应该析构d5了……
其次是静态变量,静态变量视为全局变量即可(内存时)于是自然比d1要先析构,后构造

问题5
常量方法
1)常对象,定义为
class myclass;
myclass const t;
或者是myclass const t的就是常对象,常对象t的特点是t中所有的成员变量的值都不能被修改,而且如果成员函数不是const类型的则不能调用
注意,成员函数是const的写法比较诡异,void fun() const {}这样来写。const为之写错不对……常成员函数可以访问常对象中的变量,但是依然不能修改它们
2)常成员变量以及常成员函数
注意点:常成员变量只能通过参数初始化列表来实现,而不能再函数括号之内写参数,这个比较麻烦……烦人点就是考点!!!!!
myclass(char p):c(p){}
而不是
myclass(char p)
    {
c=p;
    }这种写法过不了
3)4)比较绕……多加留意
3)指向对象的常指针
写为my class * const p=&t;(注意常指针在定义时就需要被赋值,此后再赋值会有编译错误)
此后p的地址在无法修改写为 my class  const *  p=&t;也可,但是注意这种指针不能指向常对象。如果t是按照1)中给出的常对象的话,这几句都是错的。
4)指向常对象的指针
const在最前面,const my class *  p= & t;好处是不能通过对指针取值之后来改变t的值
指向常变量的指针还可以指向一般的没有被声明为const的变量,此时,不能通过对指针取值来改变该量的大小
一些更加细节的地方:
当形参是非const的时候,实参不能传const类型的量,其他三种变化情况都可以。这可以有前面的结论推出来……
5)对象的常引用,给函数参数加上const可以避免实参的值被修改

问题6
友元友元
之间的关系不能继承……这二个比较容易混淆
还可以有友元类的概念
class T;
class M
{
friend class T;

}

 

 

问题7
运算符重载,运算符重载的基本目的就是能够让自己写的类也使用一些常见的运算符号,这样就可以以一种较为直观的方式解决问题
将运算符重载为成员函数与重载为普通函数是不一样的,重载为成员函数的时候,原来是n元运算符,现在在形参里面只用写n-1个参数,剩下的一个函数默认是接触到的类。函数重载为普通函数则是三元运算符就要重写三个实参
编译器到底是如何判断参数是几元的??真囧
一元运算符的顺序比较窘……像指针就直接写operator*()好了,
我们如果注意到常见的
一元运算符的种类,就是*,&,sizeof,强制类型转化时用的(int),(void *),以及负号-(不是减号),就会发现他们的运算符在前面,运算对象在后面,于是c++在处理一元运算符是,自动会去运算符的后面找东西,重载时直接写为 class T &operator*(),没有参数。或者是写为普通函数形式的class T &operator*(class T)
有一个例外是++,运算对象可前置又可后置。在这种情况下,class T & operator++()默认认运算对象为是后置的,即为++a;而处理前置情况的是class T & operator++(int),或者是普通函数的class T & operator++(class T ,int)
此外,重载强制类型转化(int)时,系统默认了不能写返回值类型,必须写为operator int(){return n;//n is a int type number}

class Sample {private :    int n;

public:    Sample(int i):n(i) { }    ………
};

int main()
{
Sample s(5);
cout << ( s++ ) << endl;    cout << (int) s << endl;
    cout << (int) (++s) << endl;
cout << (int) s << endl;
return 0;
}
这段函数就需要重载前置++后置++以及强制类型转换(int)。
分别写为
    Sample operator++()
    {
        n++;
        return *this;
    }
    Sample operator++(int)
    {
         Sample temp(*this);
        n++;
        return temp;
    }
    operator int()
    {
        return n;
    }
注意++在运算对象后面时,可以采用临时变量的方法。先存储未变的量,此时return 的Sample不会有问题,因为是Sample类型返回,而不是返回一个引用
operator之前没有返回值类型!!!

问题7
继承与派生(重点)
继承与派生真正体现了c++比c的优势
1)分析这一段程序
#include<iostream>
#include<string>
using namespace std;
class stu
{
protected:
    int data;
    char * name;
public:
    stu(){cout<<"base constructor"<<data<<endl;};
    stu(int  _data ,  char * _name):data(_data)
    {
        name=new char [strlen(_name)+1];
        name =  _name;
        cout<<"base constructor"<<data<<endl;
    };
    virtual void print()
    {
        cout<<data<<' '<<name<<endl;
    }
    ~stu()
    {
        cout<<"destructor "<<data<<endl;
    }
};
class destu:public stu
{
public:
    destu(int  _data , char * _name )
    {
        data=_data;
        name=new char [strlen(_name)+1];
        name =  _name;
        cout<<"derived constructor"<<data<<endl;
    }

    virtual void print()
    {
        cout<<data<<' '<<name<<endl;
    }
    ~destu()
    {
        cout<<"derived destructor "<<data<<endl;
    }
};
int main()
{

    stu b( 5 , "qwerqwerq");
    destu a( 3 ,"asdfasd");
    a.print();
    b.print();
    return 0;
}
输出是
base constructor5
base constructor2293504
derived constructor3
3 asdfasd
5 qwerqwerq
derived destructor 3
destructor 3
destructor 5

可见,派生类在构造的时候,一定是先用了一次基类的构造函数,当然在这道题中,派生类的构造函数是自己写的,没有直接显式的去用基类的构造函数,
所以先调用基类的默认构造函数,
注意!!!!!!!!!!这个时候一定要写没有参数的构造函数,stu(){cout<<"base constructor"<<data<<endl;};否则程序在生成默认函数的时候发现只有一个带参数的,然后就崩了
而如果派生类的构造函数写成这个样子
destu(int  _data , char * _name ):stu(_data, _name)
{

}
然后会输出
base constructor5
base constructor3
3 asdfasd
5 qwerqwerq
derived destructor 3
destructor 3
destructor 5
可见依然是生成了基类的构造函数。不过因为这个是显式的调用,所以编译器采用了有参数的哪个版本。
同时我们可以看到,函数在析构时,先析构了派生类,然后析构派生类生成是自动建立的基类,最后析构本来就有的定义出来的基类

2)多继承与多重继承
多重继承的时候,class  derived : public base1, public base2
先生成base1以及base2的构造函数,然后再生成derived的构造函数
如果派生类是封闭类,那么成员对象的构造函数在基类的构造函数调用结束后依次调用,最后才调用派生类的构造函数
(别忘记封闭类是什么……,
class  Big {
    private:
        int n;
        base1  b1;
        base2  b2,b3;}
封闭类对象生成时,先执行所有对象成员的构造函数,然后才执行封闭类的构造函数。多种继承容易导致二义性的出现,应该慎用(Java取消了)

多继承的时候比如上面的例子
class stu
class destu:public stu
class Restu:public destu;
这个时候,如果定义一个Restu类对象,构造时会默认生成stu以及destu的构造函数

这个例子说明了一些问题
#include<iostream>
#include<string>
using namespace std;
class Base {
public:
    int val;
    Base() { cout << "Base Constructor" << endl; }
    ~Base() {
        cout << "Base Destructor" << endl;
    }
};
class Base1:public Base
{public:
Base1(){cout<<"Base1 Constructor" << endl;}
~Base1(){cout<<"Base1 Destructor" << endl;}
};
class Base2:public Base
{
    public:
Base2(){cout<<"Base2 Constructor" << endl;}
~Base2(){cout<<"Base2 Destructor" << endl;}
};
class Derived:public Base1, public Base2 {   };
int main() {
    Derived d;
}

输出应该是
Base Constructor
Base1 Constructor
Base Constructor
Base2 Constructor
Base2 Destructor
Base Destructor
Base1 Destructor
Base Destructor

3)指针转换的基本规则:
派生类对象可以赋值给基类对象base b = d;

派生类对象可以初始化基类引用
base & br = d;

派生类对象的地址可以赋值给基类指针
base * pb = & d;
如果派生方式是 private或protected,则上述三条不可行
简单解析:d是由b派生而来,因此就想b是马,派生出了c,d,分别是白马,黑马。我们可以把白马叫马,但是不能把马叫白马,因为还有黑马的存在,确实性不能保证
base b = d;这种语句,只是说明d是可以表述成b的,但是d特有的功能b也不能使用,即d比b多出来的东西,b是不能使用的!
为了解决这种情况,我们可以把b再次强制转化成derived的对象,这样就可以使用扩展的功能了
写为
base & br = d;//此时br是基类指针,指向派生类,不能使用派生类的功能
derived e = (derived*)br;//e是强制转化而来,e就可以以使用派生类的功能了
特别注意!!!!!如果被来br指向的就是一个基类的指针,那么这种转化是极其错误的!!!!!
base & br = b;//b是基类
derived e = (derived*)br;
像这种写法就是极其错误愚蠢的……
还有这种
Derived objDerived(3);
Base objBase(5);
Derived * pDerived = (Derived *)(& objBase);
如果最后一句写成这样很明显编译都过不了……Derived * pDerived = & objBase)
但是写成原来那样,虽然可以编译通过,但是pDerived指向的东西就完全不知道是什么了……反正给Base类的内存空间里面可不一定有pDerived所要求的东西

我们为什么要进行这么诡异的转化问题研究??答案是在程序接口方面这个很重要
比如有一个怪物类,他们都是从class base_monster中派生而来
比如在具体的类,有二十种怪物都是从base_monster中派生,class monster1--class monster20.他们之间是可以互相攻击的。那么写为monster1 attack ( base_monster  x),就可以在参数中少写很多,因为每一个类的实参均满足上述的写法,都可以作为函数参数,因为他们可以赋值给派生类。如果不这样写,就要写十九个诸如此类的函数
monster1 attack ( monster2 x);
monster1 attack ( monster3 x);
………………………………
monster1 attack ( monster20 x);
这样就看出了优劣所在

 

 

问题8
多态
1)基本概念:
派生类的指针可以赋给基类指针。 通过基类指针调用基类和派生类中的同名虚函数(一种特殊的成员函数)时,若该指针指向一个基类的对象,那么被调用是基类的虚函数,如果该指针指向一个派生类的对象,那么被调用的是派生类的虚函数。这种机制就叫做“多态”。(同名很重要!!!不要忽视)
我们要注意到在不用虚函数的时候,基类指针不管指向哪一类的对象,调用同名函数的时候指向的都是基类的函数,而虚函数就可以使其访问自己指向的类的函数(虽然真实情况是一个基类指针)。这个功能其实也可以用强制类型转换完成,即每次传值进来都强制把指向派生类的基类指针强制转换为一个派生类指针,但是不够方便
class Base {
public:
    void fun1()  { fun2(); }
    void virtual fun2()  { cout << "Base::fun2()" << endl;   
           }
};
class Derived:public Base {
   virtual void fun2() { cout << "Derived:fun2()" << endl; }
};
main() {
    Derived d;
    Base * pBase = & d;
    pBase -> fun1();  
}
输出为Derived:fun2()
注意fun1根本就没有虚函数的问题,因为没有同名函数
不妨再来看这段代码
#include<iostream>
using namespace std;
class Base {
public:
    void fun1()  { fun2(); }
    virtual void  fun2()  { cout << "Base::fun2()" << endl;
           }
};
class Derived:public Base {
    void fun1(){fun2();};
     void fun2() { cout << "Derived:fun2()" << endl; }
};
int main() {
    Derived d;
    Base * pBase = & d;
    pBase -> fun1();
}
由于fun1不是虚函数,因此函数不会调用Derived中的fun1(),二十调用Base中的fun1,而由于fun2是虚函数,执行void fun1()  { fun2(); }时,由于pBase指向的是Derived类,通过动态联编,函数找到的fun2()是Derived。而不是pBase指向的fun2()fun2()

#include<iostream>
using namespace std;
class Base {
private:
    virtual void fun2()  { cout << "Base::fun2()" << endl; }
};
class Derived:public Base {
public:
   virtual void fun2() { cout << "Derived:fun2()" << endl; }
};
Derived d;
Base * p = & d;

int main()
{
p->fun2();//编译出错
return 0;
}
编译出错是因为 fun2() 是Base的私有成员。即使运行到此时实际上调用的应该是 Derived的公有成员 fun2()也不行,因为语法检查是不考虑运行结果的。
如果 将Base中的 private换成public,即使Derived中的fun2() 是private的,编译依然能通过,也能正确调用Derived::fun2()

还有引用与指针的虚函数问题还没有解决%
这个问题的答案是这样的

public继承的赋值兼容规则一共三条
*派生类的对象可以赋值给基类对象
b = d;

*派生类对象可以初始化基类引用
base & br = d;

*派生类对象的地址可以赋值给基类指针
base * pb = & d;
指针的很显然,来看其他两条
#include<iostream>
using namespace std;
class pet
{
public:
    virtual void speak()    {cout<<"pet speak"<<endl;}
};
class dog:public pet
{
    virtual void speak()    {    cout<<"wang wang"<<endl;}
};

int main()
{
    pet *p1,obj;
    dog dog1;
    p1 = &dog1;//派生类对象的地址赋值给基类指针
    p1->speak();

    obj=dog1;//派生类的对象赋值给基类对象
    obj.speak();

    pet & ref =dog1;//派生类对象初始化基类引用
    ref.speak();
}
输出结果是
wang wang
pet speak
wang wang
派生类对象的地址赋值给基类指针,根据虚函数定义显然是调用指针指向的派生类的对象赋值给基类对象,则没有考虑其是否为虚函数的问题,直接指向了基类对象的函数
派生类对象初始化基类引用时,和第一种情况相同,函数调用了派生类的虚函数

 

 

问题9
模板
建立这个概念的由来:用一个抽象的T代表具体的类,然后在需要的时候,将抽象的类里面带入具体的元素,从而实现少写代码的工作
1)函数模板
首先有函数模板的概念,比如比较大小的函数,对int类要写一个,对double要写,对float还要写。虽然由于类中可以重载函数,但是起码也需要重写几次,模板的作用就是把int ,double,float都抽象为一个统一的类,需要的时候向里面代入具体的类型,从而简化步骤。
函数模板的例子:顶头写template,代表了,里面的T代表是抽象的类
template<class T>

void print( const T array[], int size)
{
int i;

for ( i =0; i<size; i++)
cout<<array[i];

return;

}
int main()
{
int a[5]={1,2,3,4,5};
print<int>(a,5);
}
class T 的意思是c++中的数据类型都可以用class代替,像整形类,等系统自带的类以及自己定义的类。更新的写法是写为typename,
来代表这是一个抽象的c指代其他类型的符号(有点像不定元?)
template<>括号里面可以有多种参数class T1, class T2……,但是不能写int之类的东西(因为没意义,作用就是抽象的代表,写了int何谈抽象??)
要注意避免二义性的存在
函数模板也可以重载,当参数名称不一样的时候

2)类模板
当Carray里面的元素种类不能确定的时候,就可以使用模板,先抽象的定义里面的元素,然后在实际使用的时候再代入具体的类
想想stl库中的vector,明显就是这个的代表,因为vector里面可以插入多种元素,所以肯定是用了类模板,然后定义的时候写为vector<int>x之类的。
template <class T>//类模板的首部,声明类模板的参数
class Carray{
T *ptrElement;
int size;
public:
Carray(int length);
~ Carray();
int len();
void setElement(T arg, int index);
T getElement(index);
}
为类模板中各类型参数指定了具体的数据类型后,即得到一个模板类
与函数模板不同的地方是,template的定义里面里面可以有非抽象的元素,这点与函数模板不同
template <class T, int elementsNumber>
class Carray{
//T *ptrElement;
//int size;
T  elements[elementsNumber];
public:
//Carray(int length);
//~Carray();
int len();
void setElement(T arg, int index);
T getElement(index);
}

3)类模板与继承
抽象类依然可以有自己的构造函数,析构函数,也都一般的继承是一样的

*课件上的这一大堆东西比较讨厌,而且好像也比较直观
*把普通函数声明为类模板的友员函数

*利用类型参数,把模板函数声明为对应模板类的友员函数
*把其他类的成员函数声明为类模板的友员函数利用类型参数,把其他模板类的成员函数声明为对应模板类的友员函数把普通类声明为类模板的友员类
利用类型参数,
*把一个模板类声明为对应模板类的友员类
*利用类型参数,把一个模板类声明为对应模板类的友员类

每个模板类有自己的static成员,现在的模板类似乎根本就屏蔽了像课件上那样使用count的可能

 

 

问题10
输入输出流
1)流操纵算子
定义在iomanip中,像dec,oct,hex,setbase
,precision,setprecision
用户自己也可以定义流操纵算子
ostream &tab(ostream &output)
{
   return output << '\t';}

cout << “aa” << tab << “bb” << endl;
输出:aa      bb
为什么可以??

因为 iostream 里对 << 进行了重载(成员函数)

ostream & operator
  <<(  ostream & ( * p ) ( ostream & ) ) ;
(是对output << '\t'说这句话???)

posted on 2010-06-16 16:41  梦涵  阅读(284)  评论(0编辑  收藏  举报