C++ CRTP派生类对象切片

标签 c++ crtp object-slicing

我有基类 Account 和派生类 Student 和 Teacher。我在 CRTP 中实现读/写,这样我就不需要在每个类中编写函数。
奇怪的是读函数中有对象切片,函数中使用了*this,但写函数中没有。

谁能解释一下这里发生了什么?以及如何解决?

// Account.h
class Account {
public:
    Account();
    virtual void callMenu() = 0;
protected:
    int ID;
    // Some more data (Total of 148 bytes)
}

// AccountCRTP.h
template<class T>
class AccountCRTP : public Account {
public:
    // To prevent object slicing
    void callMenu() {
        T account;
        account.ID = this->ID;
        cout << "Account size: " << sizeof(account) << " bytes\n"; pause(); // 268 bytes
        if (account.readData())
            account.menu();
    }

    bool readData() {
        T account;

        cout << "Account size : " << sizeof(account) << " bytes\n"; pause(); // 268 bytes
        cout << "This size    : " << sizeof(*this) << " bytes\n"; pause();   // 148 bytes??? Why?

        while (reading data to account) {
            if (this->ID == account.ID) {
                *this = account; // Object slicing happens
                return true;
            }
        }
        return false;
    }

    bool writeData() {
        T temp;
        int position = 0l
        while (reading data to temp) {
            if (this->ID == account.ID)
                break;
            position++;
        }

        cout << "Account size : " << sizeof(temp) << " bytes\n"; pause();  // 268 bytes
        cout << "This size    : " << sizeof(*this) << " bytes\n"; pause(); // 148 bytes right?

        datafile.seekp(position * sizeof(T));

        // For unknown reason this also writes scores of the student
        // Object slicing didn't happen here. Why?
        datafile.write((char*)this, sizeof(T)); 
    }
private:
    // No additional data in this class (Still 148 bytes)
}

// Student.h
class Student :
    public AccountCRTP<Student>
{
public:
    void menu() {
        // Student menu
    }

private:
    // Some more 120 bytes of data (Total of 268 bytes)
}


// main.cpp
int main() {
    vector<unique_ptr<Account>> account;
    account.push_back(unique_ptr<Account>(new Student));
    account.push_back(unique_ptr<Account>(new Teacher));

    account[0]->callMenu();
}

最佳答案

*this = account; // Object slicing happens

“发生对象切片” 是的,您正在丢失 account 的任何特定属性(成员变量)

你真正需要的是

*static_cast<T*>(this) = account;

保留派生 T 中的内容。


static_cast<T*>(this) 技巧是使 Static Polymorphism 工作的类型安全的全部要点。
所有可能的类型检查都将在编译时应用。


让我们像 @Yakk's comment 中提到的那样,在这方面多一点语法糖:

template<typename T>
struct CRTPBase {
    T const& self() const { return *static_cast<T const*>(this); } 
    T& self() { return *static_cast<T*>(this); } 
};

那么这一行就变成了

 self() = account;

对于这种情况,即使使用 c 预处理器宏似乎也是合适的:

#define DEFINE_CRTP_SELF(derived_param) \
     derived_param const& self() const { return *static_cast<derived_param const*>(this); } 
     derived_param& self() { return *static_cast<derived_param*>(this); } 

像这样使用它:

template<typename T>
class CRTP {
public:
    DEFINE_CRTP_SELF(T);
};

注意:如果您在运行时遇到一些奇怪的事情(来自 void* 指针等),您就完蛋了。

关于C++ CRTP派生类对象切片,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42144522/

相关文章:

c++ - 如何将 SecByteBlock 转换为字符串?

c++ - 如何推断 CRTP 中的类型?

c++ - 从具有通用返回类型的 crtp 基类调用派生类中的函数

c++ - v8 持久 setWeak 回调未被调用

c++ - "error: expected constructor, destructor, or type conversion before ' ( ' token"在 memset 调用的位置

C++ 错误 1 ​​错误 C2227 : left of '->keyPress' must point to class/struct/union/generic type

C++ CRTP 问题,MSVC 错误 C2039

c++ - 覆盖虚函数返回类型不同且不是协变的

c++ - 对象切片: Access dervied class methods from base class object

c++ - 通过基本引用进行分配的对象切片是否定义良好?