c++学习 5

1 向上构造

  • 子类当作父类对象看待
  • 造型vs 类型转换:造型只是看待的眼光不同;类型转换丧失了原来的数据类型。
  • 对象里面只存储成员变量(private,public),不存储成员函数。成员函数为类所共有。并且继承类的特有成员变量都增加在后面,因此对于父类的对象的成员变量的偏移都是正确的。因此可以实现将父类对象指针指向子类。
class A
{
public:
    int i;
   
public:
    A(/* args */):i(3){};
    void f(){};
};

class B: public A
{
    private:
    int j;
    public:
    B():j(30){};
    void f(){cout<<"B::f() j="<<j<<endl;};

};

int main(){
    A a;
    B b;
 
    int *p=(int *)&b;
    cout<<p<<" "<< *p <<endl;

    p++;//让p指向j  因为增加的部分都在后面,
        //所以对于父类的偏移都是正确的,因此可以让父类的指针指向子类
    *p=100;
    b.f(); //B::f()=100 说明通过指针p 可以修改B的私有成员变量
    return 0;
}

2.多态

  • 多态性建立在upcatst动态绑定
  • virtual 子类与父类同名函数有联系。通过指针引用对象,运行时才知道调用哪个对象。
  • 动态绑定:在运行期间发生的绑定,发生动态绑定的函数的运行版本由传入的实际参数类型决定。(及运行时才知道调用哪个函数)。所有的oop 语言都默认为动态绑定,只有c++默认静态绑定。

3. 多态的实现

  • 虚类的内存分配(同一类的vtable 都一样)
    虚类的内存分配
class A{
   public:
       A(): i(10){};
       virtual void f(){cout<<"A::f()  "<<i<<endl;}
       int i;
};

int main(){
    A a;
    a.f();
    cout<<sizeof(a)<<endl;   //16

    int *p=(int *)&a;  //将类A的地址取出来,转为int型指针
    cout<<*p<<endl;    //4211952   
}
  • 同一类的vtable 都指向同一块内存区域,子类与父类的vtable的结构一样,但值不一样
int main()
{
    A a;
    A b;
    int* p=(int*)&a;
    int *q=(int*)&b;

    cout<<*p<<" "<<*q<<endl; //4211952 4211952 //说明同一类的vtable都指向同一块内存区域
    int *x=(int*)*p;
    cout<<*x<<endl;
    return 0;
}
  • **.**不会动态绑定,指针或引用才会动态绑定
class A
{
public:
    A() : i(10){};
    virtual void f() { cout << "A::f()  " << i << endl; }
    int i;
};

class B: public A
{
public:
    B(/* args */):j(1){};
    virtual void f() { cout << "B::f()  " << j << endl; }
    int j;
};
int main()
{
    A a;
    B b;
    A *p=&b; //动态绑定 当调用虚函数时,实际上是通过对象的虚函数表找到对应的函数地址进行调用
    p->f();  //B:f() 1
    return 0;
}
int main()
{
    A a;
    B b;
    a=b; //静态绑定, 由于没有默认的赋值运算重载函数,系统会自动生成default 
         //运算重载函数A& operator=(const A& other);member-member wise 的赋值
    int* p=(int*)&a;
    int *q=(int*)&b;
    cout<<*p<<" "<<*q<<endl; //4212032 4212064 因此vtable 值并没有赋值,
                            //因此无法实现动态绑定
    a.f(); //A::f() 10
    return 0;
}
int main()
{
    A a;
    B b;
    A* p=&a;
    int *r=(int*)&a;
    int *s=(int*)&b;
    *r=*s;  //将b的vtable赋值给a
    p->f(); //B::f()
    a.f(); //A::f()
    return 0;
}
  • 一个类中只要有一个virtural 函数,该类的析构函数默认就有virtrul 。
  • 在动态绑定中,使用Base::func()显示调用父类的成员函数。
  • 如果你在父类有两个virtual的overload的函数 那么你在子类也必须override所有virtutal函数的overload的函数 否则另外的函数会发生namehidding 只有c++这种oop语言存在namehide 其他语言不存在
  • 同样的函数 子类返回了子类的指针 返回了指针的引用可以 但是不能返回对象本身自己 因为只有通过指针或对象才能发生upcasting关系

4.引用再探究

  • 如果一个类的成员是reference 必须使用构造函数的列表初始化
  • 在一个函数内不能返回本地变量的地址作为指针或reference ,必须返回全局变量
int myarray[5];  //全局变量的执行,在main函数执行之前
int & subscrpit(int i){
    return myarray[i];
}
int main(){
    for(int i=0;i<5;i++){
        myarray[i]=i;
    }
    double value=subscrpit(1); //将myarray[1]的值赋给value;
    cout<<value<<endl;//"1"
    subscrpit(3) =100; //将myarray[3]赋值为100
    cout<<myarray[3]<<endl; //"100"
    return 0;
}
  • 直接传对象会浪费大量的内存空间,因此可以传递引用,为了保证调用者不修改对象,可以采用const( f(const T &))
  • A f(); 会返回对象,可以做左值,只不过不知道f()在哪里;int g();返回的是值,不能做左值。
int  f(const int&  a){
    return a*3;
}

int main(){
    int i=5;
    cout<<f(i*3);// const int @temp=i*3;
                //  f(const int& a)
    return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值