c++面向对象1

本文详细探讨了C++中虚函数的使用规则,包括构造函数内部虚函数调用的行为、继承层次中的动态绑定以及const成员函数的影响。举例说明了在构造过程中的虚函数调用只会绑定到当前类,而非子类,以及const修饰成员函数的差异。同时,文章还涉及了操作符重载和默认参数在虚函数中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

class Base {

public:
    int get_i() {
        return m_i;
    }
    void set_i(int i) {
        m_i = i;
    }
private:int m_i;
};
int main()
{
    Base b;
    b.set_i(1);
    cout << b.get_i() <<endl;
    //cout << b.m_i <<endl;//compile error
    //cannot access private member
}

class CParent
{
public:
    CParent() {}//step2
    virtual ~CParent() {}
public:
    void Print() { std::cout << "1,"; }
};
class CSon : public CParent
{
public:
    CSon() {}//step1 step4
    virtual ~CSon() {}
public:
    virtual void Print() { std::cout << "2,"; }
};

int main()
{
    CParent * pParent = new CSon();
    pParent->Print();
    delete pParent;
    return 0;
}

输出为1,
virtual只对自己以下的继承有效,对之上的继承无效。


类中调用自己的成员 虚函数。

class CParent
{
public:
    CParent() {Print();}//step2
    virtual ~CParent() {}
public:
    virtual void Print()
    {
        std::cout << "1,";//step3
    }
    void print1() {Print();}//step2
};
class CSon : public CParent
{
public:
    CSon() {Print();}//step1 step4
    virtual ~CSon() {}
public:
    void Print()
    {
        std::cout << "2,";//step5
    }
    void print1() {Print();}//step2
};

int main()
{
    CParent * pParent = new CSon();//(对象)
    pParent->print1();
    delete pParent;
    return 0;
}

以上结果输出为1,2,2

原因:继承关系中,构造调用顺序从基类到派生类,类调用本地版本,因为虚函数在构造中不起作用,为什么?原因很简单,构造过程中派生类的信息还没有存在,如果调用将非常危险
C++标准规定了在构造函数中调用虚函数时,只会绑定到自己类的相应函数,因为即使此时是一个子类的对象构造,但在执行父类的构造函数是,子类的变量还没有初始化,虚函数表可能都还没有构造好,所以,标准里禁止此时调用子类的虚函数。

class CParent
{
public:
    CParent() {
        Print();
    }
    virtual ~CParent() {}
public:
    virtual void Print() = 0;
};
class CSon : public CParent
{
public:
    CSon() {Print();}
    virtual ~CSon() {}
public:
    void Print()
    {
        std::cout << "2,";
    };
};
int main() {
    CParent *pParent = new CSon();
    delete pParent;
}

这道题不能通过编译,与第上题类似,在构造函数里,不会进行动态绑定,即使是虚函数。而这时,基类的构造函数里调用Print是基类本身的,但Print没有实现,所以
编译器不能通过链接。若基类中去掉Print函数,则能编译成功

class Base
{
public:
    virtual bool operator == (int iValue)
    {
        std::cout << "I am Base class !" << std::endl;
        return true;
    }
    virtual ~Base(){}
};
class Derive: public Base
{
public:
    virtual bool operator == (int iValue)
    {
        std::cout << "I am Derive class !" << std::endl;
        return true;
    }
    virtual ~Derive(){}
};
int main()
{
    Derive derive;
    Base* pBase = &derive;
    Derive* pDerive = &derive;
    *pBase == 0;
    *pDerive == 0;
    return 0;
}

程序的输出结果是____:
I am Derive class !
I am Derive class !

操作符重载函数也是可以是虚函数的。

class Base
{
public:
    virtual void display(std::string strShow = "Base default string!")
    {
        std::cout << strShow << std::endl;
        std::cout <<"In Base"<< std::endl;
    }
    virtual ~Base(){}
};
class Derive: public Base
{
public:
    virtual void display(std::string strShow = "Derive default string!")
    {
        std::cout << strShow << std::endl;
        std::cout <<"In Derive"<< std::endl;
    }
    virtual ~Derive(){}
};

int main()
{ Base* pBase = new Derive(); Derive* pDerive = new Derive();
    pBase->display();
    pDerive->display();
    delete pBase;
    delete pDerive;
    return 0;
}

I am Base class !
In Derive
I am Derive class !
In Derive

虚函数是动态绑定,但默认值是在编译期决定的。对pBase->display进行调用时,实际还是调用的Derive::display,但传入的默认参数却是参照Base::display的。

关于const修饰函数返回值的测试


class Base
{
public:

    // if there is display(), then display()
    // if there is no display(), then display() const
    virtual void display()
    {
        std::cout << "I am Base class display!" << std::endl;
    }
    virtual void display() const
    {
        std::cout << "I am Base class display const!" << std::endl;
    }

    virtual int display1(const int i)
    {
        std::cout << "I am Base class display1!" << std::endl;
        return 1;
    }


    //virtual int display1(int i)
    //int display1(int i)
    // class member cannot be redeclared
    //const int display1(const int i)
    //char display1(const int i)
    // functions that differ only in return type cannot be overload

    // const修饰返回值为值的时候没有意义,只有在修饰返回值为指针的时候才有意义。
    // 返回值为值的时候,重新赋值之后约束不到对方。
    // 返回值为指针的时候,重新赋值的时候强制对方也为const
    const int display2()
    {
        int result = 1;
        std::cout << "I am Base class display2!" << std::endl;
        return result;
    }

    //指针必须为const
    const char* display3()
    {
        char* result = new char(10);
        memset(result, 0, 10);
        result[0] = 'a';
        result[1] = 0;
        return result;
    }

    //没有意义,全依赖重新赋值时候的约束
    char* const display4()
    {
        char* result = new char(10);
        memset(result, 0, 10);
        result[0] = 'b';
        result[1] = 0;
        return result;
    }

    virtual ~Base(){}
};

int main()
{
    Base* pBase = new Base();
    pBase->display();
    pBase->display1(1);
    int int1 = pBase->display2();
    int1++;
    std::cout << int1 <<endl;

    //char* char1 = pBase->display3();
    //char* const char1 = pBase->display3();
    // Cannot initialize a variable of type 'char *' with an rvalue of type 'const char *'

    const char* char1 = pBase->display3();
    printf("%s\n", char1);
    //char1[0] = 'c';
    // readobly variable is not assaignable


    const char* char2 = pBase->display4();
    printf("%s\n", char2);
    //char2[0] = 'c';
    // readobly variable is not assaignable
    char2 = new char(10);


    char* const char3 = pBase->display4();
    char3[0] = 'c';
    printf("%s\n", char3);
    //char3 = new char(10);
    // cannot assign to variable 'char3' with const-qualified type 'char *const'

    delete pBase;
    return 0;
}

输出为:
I am Base class display!
I am Base class display1!
I am Base class display2!
2
a
b
c

class Base
{
public:
    virtual void display() const
    {
        std::cout << "I am Base class !" << std::endl;
    }
    virtual int display1(const int i)
    {
        std::cout << "I am Base class !" << std::endl;
        return 1;
    }
    virtual const int display2()
    {
        int result = 1;
        std::cout << "I am Base class !" << std::endl;
        return result;
    }
    virtual ~Base(){}
};
class Derive: public Base
{
public:
    // if there are both const or not const the all derived function
    virtual void display() //const 
    {
        std::cout << "I am Derive class !"<< std::endl;
    }
    virtual int display1(int i)
    {
        std::cout << "I am Derive class !" << std::endl;
        return 1;
    }
    virtual const int display2()
    //virtual int display2()//compile failed
    //virtaul function display2 have diferrent return type ('int') than
    //the function it overrides (which has a return type 'const int)
    {
        int result = 1;
        std::cout << "I am Derive class !" << std::endl;
        return result;
    }
    virtual ~Derive(){}
};
int main()
{
    Base* pBase = new Derive();
    Derive* pDerive = new Derive();
    pBase->display();
    pDerive->display();
    pBase->display1(1);
    pDerive->display1(1);
    delete pBase;
    delete pDerive;
    return 0;
}

I am Base class !
I am Derive class !


I am Derive class !
I am Derive class !

加了const以后,是两个不同的函数,Derive实际上有三个虚函数。如果基类virtual void display() const中的const去掉,则子类相当于重载了此基类函数,答案就是

I am Derive class !
I am Derive class !
首先要认识到这是两个不同的函数
其次,通过Base *p去调用display,这个display必须是基类声明的display,因为基类的指针不可能调用到派生类声明的方法,它根本不可能知道派生类有哪些方法,所以 pBase->display();只会调用基类的方法(如果派生类重写了,则根据多态会调用派生类的实现),这个地方不能把多态加进来,多态是运行期的概念,但编译的时候,pBase->display()就被绑定到display const这个方法了(不可能说因为pBase实际上是一个派生类的对象来进行绑定),由于派生类没有重载display const这个方法,输出为”I am base class !”
pDerive->display();调用因为pDerive不是const的,绑定到非const的方法,可试试把pDerive加上const修饰符,输出就会变的
"virtual void display() const"与"virtual void display()"是不同的两个接口,就如同"virtual void display_const()"与"virtual void display()"是两个接口一样。
因此在Derive中,实际上有个虚函数
"virtual void display() const"
"virtual void display()"
"virtual ~Derive()"
并且,"virtual void display() const"是从父类继承下来的,而且没有重写它。
因此pBase->display()虽然调用的是子类的"virtual void display() const"函数,但子类没有重写它,实际还是调用的父类的"virtual void display() const"函数。
而pDerive->display()则优先调用自己类的"virtual void display()"函数。

另外,对于const 修饰函数的三个位置的思考,

修饰函数返回值:如果基类和派生类不一致,会产生编译失败;

修饰入参:不影响,认为是一样的;

修饰函数本身,认为是不一样的。

const函数的使用

class Base
{
public:
    void display()
    {
        std::cout << "I am display function!" << std::endl;
        m_i = 0;
        //m_j = 0;//compile failed
        //cannnot assign to non-static data member 'm_j' with const-qualified type 'const int'
    }
    void display() const
    {
        std::cout << "I am display const function!" << std::endl;
        //m_i = 0;//compile failed
        //cannot assign to non-static data member within const member function display.
    }
    int display1(const int i)
    {
        std::cout << "I am display1 function!" << std::endl;
        //i = 2;//compile failed
        //cannot assign to variable "i" with consit -qualified type 'const int'
        return 1;
    }
    const int display2()
    {
        int result = 1;
        std::cout << "I am display1 function!" << std::endl;
        return result;
    }
    //void display2(){}//compile failed
    //int display2(){}//compile failed
    //functions that differ only in their return type cannot be overloaed

    void display3() const
    {
        std::cout << "I am display3 const function!" << std::endl;
        //m_i = 0;//compile failed
        //cannot assign to non-static data member within const member function display.
    }



    ~Base(){}

private:
    int m_i;
    //const int m_j;//compile failed
    //when instantialize, canll to implicitlly default constaructor of 'Base'
    const int m_j = 0;
};

int main()
{
    //*
    Base* pBase = new Base();
    const Base* cBase = new Base();

    pBase->display();
    cBase->display();

    pBase->display1(1);
    //cBase->display1(1);//compile failed
    //this argument to member function displayq has type 'const base' but function is not marked const

    pBase->display3();
    cBase->display3();

    delete pBase;
    delete cBase;
     //*/

    return 0;
}

https://www.cnblogs.com/2018shawn/p/10853607.html

1.const 修饰类的成员变量,表示成员常量,不能被修改。

2.const修饰函数承诺在本函数内部不会修改类内的数据成员,不会调用其它非 const 成员函数。

3.如果 const 构成函数重载,const 对象只能调用 const 函数,非 const 对象优先调用非 const 函数(没有非const函数则调用const函数)。

4.const 函数只能调用 const 函数。非 const 函数可以调用 const 函数。

5.类体外定义的 const 成员函数,在定义和声明处都需要 const 修饰符

6.const在*的左边,则指针指向的变量的值,不可直接通过指针改变(可以通过其他途径改变);

    在*的右边,则指针的指向不可变。简记为“左定值,右定向”。

class A
{
public:
    void Func() {} // 如果没有{}, 则无法解析外部命令
    void Func2() {cout<<"A::Func2"<<endl;};
};
class B
{
private:
    bool Func() const {}
    bool Func2() const {;};
};
class C:
        public A,
        public B
{

}; // class definition is unimportant


int main()
{
    C test;
    //test.Func(); //look here
    //Member 'Func' found in multiple base classes of different types
    //member found by ambiguous name lookup
}


A.test.B::Func(); B.test.A::Func();
C.B::test.Func(); D.A::test.Func();
考察多重继承的问题,B的Func函数是私有的,所以A不能正确访问。

对于下面的代码,描述正确的是:

class A
{
public:
    virtual void test();
};
class B: public A
{
public:
    void test();
    //...
};
class C: public B
{
public:
    void test();
    //...
};
A.B类的test函数是虚函数,而C类的也是
B.B类的test函数不是虚函数
C.B类的test函数是虚函数,而C类的不是
D.C类的test函数不是虚函数
基本概念。如果父类里声明了某函数为虚函数,则在子类此函数的声明里不管有没有"vitrual"关键子,都是虚函数。即使访问权限发生变化。如C声明为:
class C: public B
{
private:
    void test();
    ...
};
则test一样是虚函数。

以下程序的输出是:


class Base
{
public:
    int a = 0;
    Base() { std::cout << __FUNCTION__ << std::endl; }
    virtual ~Base() { std::cout << __FUNCTION__ << std::endl; }
    virtual void print() { std::cout << __FUNCTION__  << " " << a << std::endl; }
};

class Derive1:public Base
{
public:
    int a = 1;
    int d1 = 10;
    Derive1() { std::cout << __FUNCTION__ << std::endl; }
    ~Derive1() { std::cout << __FUNCTION__ << std::endl; }
    virtual void print1() { std::cout << __FUNCTION__  << " " << a << std::endl; }
};

class Derive2 : public Derive1
{
public:
    int a = 2;
    int d2 = 20;
    Derive2() {std::cout << __FUNCTION__ << std::endl; }
    ~Derive2() {std::cout << __FUNCTION__ << std::endl; }
    virtual void print2() { std::cout << __FUNCTION__  << " " << a << std::endl; }
};

int main()
{

    /*
    Derive1* p1 = new Derive2();
    p1->print();
    ((Derive1*)p1)->print1();
    ((Derive2*)p1)->print2();
     //*/
    
    /*
    Derive2 d2;
    Derive1& p1 = d2;
    cout << p1.a << p1.Base::a;

    Derive2 d3;
    Derive1* p2 = &d3;
    cout << p2->a << p2->Base::a << ((Derive2*)p2)->Derive2::a;
     //*/

    Derive1* pObj = new Derive2();
    delete pObj;
    return 0;
}

Base::Base
Derive1::Derive1
Derive2::Derive2
Derive2::~Derive2
Derive1::~Derive1
Base::~Base

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值