首页 > 解决方案 > 我有一个向量,它具有指向通过接口访问的 2 个子类型的元素的指针。似乎无法重新排列矢量

问题描述

我正在为学校设计一个真正过度设计的矩阵计算器,并用我需要的最后一个功能撞到了一堵砖墙。的完整版本transpose()应该重新排列我的矩阵并切换列和行。

我之前用一个片段发布了这个问题,但被要求写一个最低限度的可重现样本。这不是很“最低限度”,但我试过了。

所以。我有一个std::vector<std::unique_ptr<Interface>>可以容纳类型Sub1Sub2.

在完整版中,我有一个类可以绕过Interface并且工作正常,因为它只包含Sub2对象。

但是,当向量中有多种类型时,我似乎无法让它工作。在过去的几个小时里我尝试过的任何事情都没有做任何事情。

#include <iostream>
#include "main.h"
#include <memory>
#include <utility>
#include <vector>
#include <sstream>


//this is the one that won't work
Super Super::transpose() const {
    Super newMatrix = *this;
    int i = 1;
    for(auto& elem : elements){
        *elem = *newMatrix.elements[i];
        i--;
    }
return *this;
}


int main() {
    std::vector<std::unique_ptr<Interface>> newMatrix;
    newMatrix.push_back(std::unique_ptr<Sub1>(new Sub1('x')));
    newMatrix.push_back(std::unique_ptr<Sub2>(new Sub2(1)));
    Super mtx(std::move(newMatrix),2 );
    std::cout << mtx << std::endl;
    mtx.transpose();
    std::cout << mtx;
    return 0;
}

//busywork from here on 
Super::Super(std::vector<std::unique_ptr<Interface>> matrix, int pN){
    elements = std::move(matrix);
    n = pN;
}

std::unique_ptr<Interface> Sub1::clone() const {
    char newVal = val;
    return std::unique_ptr<Sub1>(new Sub1(newVal));
}

std::unique_ptr<Interface> Sub2::clone() const {
    int newVal = i;
    return std::unique_ptr<Sub2>(new Sub2(newVal));
}

Sub1::Sub1(char pVal) {
    val = pVal;
}

Sub2::Sub2(int pI) {
    i = pI;
}

std::string Sub1::toString() const {
    std::stringstream ss;
    ss << val;
    return ss.str();
}

std::string Sub2::toString() const {
    std::stringstream ss;
    ss << i;
    return ss.str();
}

std::ostream &operator<<(std::ostream &os, const Super &matrix) {
    os << "[";
    int i = 0;
    for (auto &elem : matrix.elements) {
        os << elem->toString();
        if (i < matrix.n - 1) {
            os << ",";
        }
        i++;
    }
    os << "]";
    return os;
}

Super::Super(const Super &matrix) {
    n = matrix.n;
    for (auto &elem : matrix.elements) {
        std::unique_ptr<Interface> newElem = elem->clone();
        elements.push_back(std::move(newElem));
    }
}

这是标题

#ifndef EXAMPLE_MAIN_H
#define EXAMPLE_MAIN_H

#include <memory>
#include <vector>


class Interface{
public:
    virtual ~Interface() = default;
    virtual std::unique_ptr<Interface> clone() const = 0;
    virtual std::string toString() const = 0;
};

class Super{
private:
    std::vector<std::unique_ptr<Interface>> elements;
    int n = 2;
public:
    Super(std::vector<std::unique_ptr<Interface>> elements, int n);
    Super(const Super &matrix);
    Super transpose() const;
    friend std::ostream &operator<<(std::ostream &os, const Super &matrix);
};

class Sub1: public Interface{
private:
    char val;
public:
    Sub1(char pVal);
    std::string toString() const override;
    std::unique_ptr<Interface> clone() const override;
};


class Sub2: public Interface{
private:
    int i;
public:
    Sub2(int pI);
    std::string toString() const override;
    std::unique_ptr<Interface> clone() const override;
};


std::ostream &operator<<(std::ostream &os, const Super &matrix);

#endif //EXAMPLE_MAIN_H

标签: c++c++11

解决方案


Super Super::transpose() const;

这看起来像一个函数,*this在调用时不应更改,但应返回*this.

例子:

#include <algorithm>

Super Super::transpose() const {
    Super newMatrix = *this;
    std::reverse(newMatrix.elements.begin(), newMatrix.elements.end());
    return newMatrix;
}

如果您想*this就地转置,请将其更改为:

Super& Super::transpose();

Super& Super::transpose() {
    std::reverse(elements.begin(), elements.end());
    return *this;
}

如果必须先创建一个临时对象,则可以,但不能将 dereferenced 分配Interface*给另一个 dereferenced Interface*。虽然移动unique_ptr作品:

Super& Super::transpose() {
    Super newMatrix = *this;
    size_t s = elements.size();
    for(size_t i = 0; i < s; ++i) {
        elements[i] = std::move(newMatrix.elements[s - i - 1]);
    }
    return *this;
}

演示

您还可以将 封装std::unique_ptr<Interface>在包装类中以添加复制构造函数和赋值运算符(不支持std::unique_ptr)。通过这样做,您可以大大简化您的其他类。你的Super班级不需要知道任何关于克隆的知识。复制/移动元素可以开箱即用。

示例包装器:

class Cell {
public:
    Cell() noexcept = default;                              // empty Cell ctor
    explicit Cell(std::unique_ptr<Interface>&& d) noexcept; // converting ctor

    // rule of five
    Cell(const Cell& rhs);                           // must be implemented
    Cell(Cell&& rhs) noexcept = default;             // handled by unique_ptr
    Cell& operator=(const Cell& rhs);                // must be implemented
    Cell& operator=(Cell&& rhs) noexcept = default;  // handled by unique_ptr
    ~Cell() = default;                               // handled by unique_ptr

    explicit operator bool() const noexcept; // proxy for unique_ptr operator bool
    void reset() noexcept;                   // empty the Cell

    // dereferencing
    Interface& operator*();
    const Interface& operator*() const;
    Interface* operator->();
    const Interface* operator->() const;

    std::ostream& print(std::ostream& os) const;     // calls: os << data->toString()

    // A helper factory to make a Cell of a certain type using the converting ctor
    template<typename T, class... Args>
    static Cell make(Args&&... args) {
        return Cell(std::make_unique<T>(std::forward<Args>(args)...));
    }

private:
    std::unique_ptr<Interface> data{};
};

std::ostream& operator<<(std::ostream& os, const Cell& c); // calls c.print(os)

复制构造函数和复制赋值运算符可以这样实现:

Cell::Cell(const Cell& rhs) : data(rhs ? rhs.data->clone() : nullptr) {}

Cell& Cell::operator=(const Cell& rhs) {
    if(this != &rhs) data = rhs ? rhs.data->clone() : nullptr;
    return *this;
}

这是一个使用std::vector<std::vector<Cell>>. 这样的 2D 矢量不是很有效,但它可以用于演示的目的。


推荐阅读