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

C++ 複製與虛擬複製:避免容器中物件切割

     編輯:關於C++

問題:容器存放基類別物件導致切割

當以 by-value 方式將派生類別物件放入基類別容器(如 std::vector<Quote>),會發生物件切割,僅保留基類別部分,虛函數失去多態行為。

解決方案一:虛擬複製函式

在基類別定義虛擬 clone() 函式,回傳 Quote*(或智慧指標)。派生類別覆寫 clone() 回傳自身型別指標。容器存放 std::shared_ptr<Quote>,呼叫 clone() 即可獲得正確的派生類別副本。

解決方案二:直接使用智慧指標存放

容器型別改為 std::vector<std::shared_ptr<Quote>>,以 std::make_shared<Derived>() 建立物件,自動實現動態綁定,無需手動實作 clone()。

程式碼示範

/* CppPrimer.cpp
 * Created on: 2013.11.12
 * Author: Caroline
 */
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <utility>
#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* clone() const & { return new Quote(*this); }
    virtual Quote* clone() && { return new Quote(std::move(*this)); }
    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;
    virtual Bulk_quote* clone() const & { return new Bulk_quote(*this); }
    virtual Bulk_quote* clone() && { return new Bulk_quote(std::move(*this)); }
};

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;
}

class Basket {
public:
    void add_item(const std::shared_ptr<Quote>& sale) { items.insert(sale); }
    void add_item(const Quote& sale) {
        items.insert(std::shared_ptr<Quote>(sale.clone()));
    }
    void add_item(Quote&& sale) {
        items.insert(std::shared_ptr<Quote>(std::move(sale).clone()));
    }
    double total_reciept(std::ostream&) const;
private:
    static bool compare(const std::shared_ptr<Quote>& lhs,
                        const std::shared_ptr<Quote>& rhs) {
        return lhs->isbn() < rhs->isbn();
    }
    std::multiset<std::shared_ptr<Quote>, decltype(compare)*> items{compare};
};

double Basket::total_reciept(std::ostream& os) const {
    double sum = 0.0;
    for (auto iter = items.cbegin(); iter != items.cend();
         iter = items.upper_bound(*iter)) {
        sum += print_total(os, **iter, items.count(*iter));
    }
    os << "Total Sale: " << sum << std::endl;
    return sum;
}

int main() {
    Basket bsk;
    bsk.add_item(Quote("CppPrimer", 45));
    bsk.add_item(Bulk_quote("EffectiveCpp", 50, 2, 0.15));
    bsk.add_item(Bulk_quote("EffectiveCpp", 50, 2, 0.15));
    bsk.add_item(Bulk_quote("EffectiveCpp", 50, 2, 0.15));
    bsk.total_reciept(std::cout);
    return 0;
}

輸出結果

ISBN: CppPrimer # sold: 1 total due: 45
ISBN: EffectiveCpp # sold: 3 total due: 127.5
Total Sale: 172.5

結語

虛擬複製函式配合智慧指標,能在容器中正確保存派生類別物件,實現多態。現代 C++ 更推薦直接使用 std::shared_ptr 存放,簡化設計並確保資源管理安全。

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