为什么从基础转换为派生赋予此功能?

问题描述 投票:0回答:1

我很困惑为什么当我没有使用virtual关键字时,将派生类转换为派生类方法的基类调用指针。这是正常的行为吗?指针是否在内存中保存Person对象,因此将其转换为Student不应该对其内容产生任何影响?

  class Person {

    public:

    Person()
    {
        cout << "Creating Person Class" << endl;
    }

    void about_me()
    {
        cout << "I am a person" << endl;
    }
};

class Student : protected Person {
    public:
    Student()
    {
        cout << "Creating Student Class" << endl;
    }

    void about_me()
    {
        cout << " I am a student " << endl;
    }

};

int main()
{
    Person* pperson = new Person();
    Student* pstudent = new Student();

    pperson->about_me();
    pstudent->about_me();

    pperson-> about_me();

    ((Student*)pperson)-> about_me(); // this is the line where I do the cast

    return 0;

}

代码的输出如下

  Creating Person Class
Creating Person Class
Creating Student Class
I am a person
 I am a student 
I am a person
 I am a student
c++ casting base derived
1个回答
4
投票

你的代码“有效”,因为你的about_me()方法都没有访问各自的this指针。

从技术上讲,你正在做的是未定义的行为,因为pperson没有指向有效的Student对象,但是你告诉编译器将它视为它。所以任何事情都可能发生。

在许多常见的编译器实现中,像pperson->about_me()这样的类方法调用实际上更像是about_me(pperson),其中about_me()被实现为具有this输入参数的独立函数。因此,您所展示的代码可能是编译器实现的更多内容(不完全是这样,但您应该明白这一点):

struct Person
{
};

void Person_Person(Person *this)
{
    cout << "Creating Person Class" << endl;
}

void Person_about_me(Person *this)
{
    cout << "I am a person" << endl;
}

struct Student
{
};

void Student_Student(Student *this)
{
    Person_Person(this);
    cout << "Creating Student Class" << endl;
}

void Student_about_me(Student *this)
{
    cout << " I am a student " << endl;
}

int main()
{
    //Person* pperson = new Person();
    byte *buf1 = new byte[sizeof(Person)];
    Person* pperson = (Person*) buf1;
    Person_Person(pperson);

    //Student* pstudent = new Student();
    byte *buf2 = new byte[sizeof(Student)];
    Student* pstudent = (Student*) buf2;
    Student_Student(pstudent);

    //pperson->about_me();
    Person_about_me(pperson);

    //pstudent->about_me();
    Student_about_me(pstudent);

    //pperson-> about_me();
    Person_about_me(pperson);

    //((Student*)pperson)-> about_me();
    Student_about_me((Student*)pperson);

    return 0;
}

因此,在第4次调用about_me()时,你指示编译器调用Student::about_me()而不是让它正常调用Person::about_me(),其this参数设置为Person*指针,类型转换为Student*。由于this没有被about_me()取消引用,所以这个电话是“成功的”,因为你看到了“预期的”输出。在这种情况下this指向的并不重要,因为它没有被使用。

现在,尝试将一些数据成员添加到类中,然后在about_me()中输出这些成员,由于您正在调用未定义的行为,您将看到非常不同的,非常意外/随机的结果。例如:

class Person
{
protected:
    string m_name;

public:

    Person(const string &name)
        : m_name(name)
    {
        cout << "Creating Person Class" << endl;
    }

    void about_me()
    {
        cout << "I am a person, my name is " << m_name << endl;
    }
};

class Student : protected Person
{
private:
    int m_id;
    string m_school;

public:

    Student(const string &name, int id, const string &school)
        : Person(name), m_id(id), m_school(school)
    {
        cout << "Creating Student Class" << endl;
    }

    void about_me()
    {
        cout << "I am a student, my name is " << m_name << ", my id is " << m_id << " at " << m_school << endl;
    }
};

int main()
{
    Person* pperson = new Person("John Doe");
    Student* pstudent = new Student("Jane Doe", 12345, "Some School");

    pperson->about_me(); // "I am a person, my name is John Doe"
    pstudent->about_me(); // "I am a student, my name is Jane Doe, my id is 12345 at Some School"

    pperson->about_me(); // "I am a person, my name is John Doe"

    ((Student*)pperson)->about_me(); // runtime error!

    delete pstudent;
    delete pperson;

    return 0;
}

Live Demo

© www.soinside.com 2019 - 2024. All rights reserved.