首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 容器中正確使用繼承與虛函數:避免物件切割

     編輯:關於C++

前言

在 C++ 中,標準容器(如 std::vector)預設存放的是物件實體(value semantics)。若直接將派生類別物件放入基類別容器,會發生「物件切割」,導致派生類別的特有資料與虛函數行為遺失。本文說明為何會發生切割,並示範如何透過指標(含智慧指標)在容器中實現多態。

為何會發生物件切割?

  • 容器元素型別固定為基類別(如 Quote),編譯器只分配基類別大小的記憶體。
  • 派生類別物件(如 Bulk_quote)以 by-value 方式存入時,只有基類別部分被複製,派生類別擴充的資料成員與虛函數表指標被「切掉」。
  • 轉換只能發生在指標或參考上,直接物件賦值會導致切割。

解決方案:使用指標或智慧指標

將容器元素型別改為基類別指標(Quote*)或智慧指標(std::shared_ptr<Quote>),即可保留完整的派生類別物件,並在呼叫虛函數時動態綁定到派生類別的覆寫版本。

程式碼示範

/* CppPrimer.cpp
 * Created on: 2013.11.12
 * Author: Caroline
 */
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <cstddef>
using namespace std;

class Quote {
public:
    Quote() = default;
    Quote(const std::string& book, double sales_price)
        : bookNo(book), price(sales_price) {}
    std::string isbn() const { return bookNo; }
    virtual double net_price(std::size_t n) const { return n * price; }
    virtual ~Quote() = default;
private:
    std::string bookNo;
protected:
    double price = 0.0;
};

class Disc_quote : public Quote {
public:
    Disc_quote() = default;
    Disc_quote(const std::string& book, double price,
               std::size_t qty, double disc)
        : Quote(book, price), quantity(qty), discount(disc) {}
    double net_price(std::size_t) const = 0; // 純虛函數
protected:
    std::size_t quantity = 0;
    double discount = 0.0;
};

class Bulk_quote final : public Disc_quote {
public:
    Bulk_quote() = default;
    Bulk_quote(const std::string& book, double p,
               std::size_t qty, double disc)
        : Disc_quote(book, p, qty, disc) {}
    double net_price(std::size_t cnt) const override;
};

double Bulk_quote::net_price(std::size_t cnt) const {
    if (cnt >= quantity)
        return cnt * (1 - discount) * price;
    else
        return cnt * price;
}

double print_total(std::ostream& os, const Quote& item, std::size_t n) {
    double ret = item.net_price(n);
    os << "ISBN: " << item.isbn()
       << " # sold: " << n
       << " total due: " << ret << std::endl;
    return ret;
}

int main() {
    // 直接存放物件:會切割,呼叫基類別虛函數
    std::vector<Quote> basket;
    basket.push_back(Quote("CppPrimer", 50));
    basket.push_back(Bulk_quote("CppPrimer", 50, 10, .25));
    std::cout << "Bulk_quote (sliced): "
              << basket.back().net_price(20) << std::endl;

    // 使用 shared_ptr:正確多態
    std::vector<std::shared_ptr<Quote>> sbasket;
    sbasket.push_back(std::make_shared<Quote>("CppPrimer", 50));
    sbasket.push_back(std::make_shared<Bulk_quote>("CppPrimer", 50, 10, .25));
    std::cout << "Bulk_quote (polymorphic): "
              << sbasket.back()->net_price(20) << std::endl;
    return 0;
}

執行結果

Bulk_quote (sliced): 1000
Bulk_quote (polymorphic): 750

第一行顯示切割後僅呼叫基類別 net_price(50 * 20 = 1000);第二行正確呼叫 Bulk_quote::net_price,享受折扣(50 * 20 * 0.75 = 750)。

結語

在需要多態的容器中,務必使用指標或智慧指標,避免物件切割導致的行為錯誤。現代 C++ 建議優先使用 std::shared_ptr 或 std::unique_ptr 管理物件生命週期,確保資源安全與正確的動態綁定。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved