C ++错误“左操作数必须是l值”

时间:2015-02-07 22:28:26

标签: c++ arrays pointers lvalue

我正在尝试编写一个C ++程序,以便解决Rubik的多维数据集。我定义了四个类:Piece,Edge,Corner和Cube,其中Corner和Edge是Piece的子类。

Cube类定义如下:

class Cube{
private:
    Piece* pieces[3][3][3];
public:
    Corner* WRG = new Corner(WHITE, RED, GREEN, WHITE);
    Corner* WGO = new Corner(WHITE, GREEN, ORANGE, WHITE);
    Corner* WOB = new Corner(WHITE, ORANGE, BLUE, WHITE);
    Corner* WBR = new Corner(WHITE, BLUE, RED, WHITE);

    Corner* YRB = new Corner(YELLOW, RED, BLUE, YELLOW);
    Corner* YBO = new Corner(YELLOW, BLUE, ORANGE, YELLOW);
    Corner* YOG = new Corner(YELLOW, ORANGE, GREEN, YELLOW);
    Corner* YGR = new Corner(YELLOW, GREEN, RED, YELLOW);

    Edge* WR = new Edge(WHITE, RED, WHITE);
    Edge* WB = new Edge(WHITE, BLUE, WHITE);
    Edge* WO = new Edge(WHITE, ORANGE, WHITE);
    Edge* WG = new Edge(WHITE, GREEN, WHITE);

    Edge* YR = new Edge(YELLOW, RED, YELLOW);
    Edge* YB = new Edge(YELLOW, BLUE, YELLOW);
    Edge* YO = new Edge(YELLOW, ORANGE, YELLOW);
    Edge* YG = new Edge(YELLOW, GREEN, YELLOW);

    Edge* GO = new Edge(GREEN, ORANGE, GREEN);
    Edge* GR = new Edge(GREEN, RED, GREEN);

    Edge* BO = new Edge(BLUE, ORANGE, BLUE);
    Edge* BR = new Edge(BLUE, RED, BLUE);

    Cube();
    ~Cube();
    void rotateRedClock();
    void rotateRedCounter();
    void rotateOrangeClock();
    void rotateOrangeCounter();
    void rotateYellowClock();
    void rotateYellowCounter();
    void rotateGreenClock();
    void rotateGreenCounter();
    void rotateBlueClock();
    void rotateBlueCounter();
    void rotateWhiteClock();
    void rotateWhiteCounter();

    void doMove(int);
    Piece getPieces();
    Cube* getChildren();
};
Cube::Cube(){
    Piece* pieces[3][3][3] = { { { WRG, WR, WBR }, { GR, NULL, BR }, { YGR, YR, YRB } }, //Red face
                            { { WG, NULL, WB }, { NULL, NULL, NULL }, { YG, NULL, YB } }, //Middle section
                            { { WGO, WO, WOB }, { GO, NULL, BO }, { YOG, YO, YBO } } }; //Orange face
}

此数组存储在Cube对象内部,该对象可以对数组中的指针进行随机化,并更改每个Piece的方向参数以处理旋转。据我所知,这一切都应该可以正常工作。

当我尝试返回包含当前状态下所有可能移动的Cube对象数组时,问题就开始了。

如果我用Java编程,它看起来像这样:

public Cube[] getChildren(){
    Cube children = new Cube[12];
    for (int i = 0; i < 12; i++){
        children[i] = new Cube(this.getPieces()); //Effectively clone this
        children[i].doMove(i); //Does one of the 12 available moves on the cube
    }
    return children;
}

然而,在C ++中,我似乎无法实现这一目标。我尝试了以下方法:

Cube* Cube::getChildren(){
    Cube* children = new Cube[12];
    for (int i = 0; i < 12; i++){
        children[i] = Cube();
        children[i].pieces = pieces;
        children[i].doMove(i);
    }
    return children;
}

但我在这一行上收到错误:

children[i].pieces = pieces;

它说:

error C2106: '=' : left operand must be l-value

我是C ++的新手,这个错误可能是由于我对某些概念缺乏了解所致。我想知道我做错了什么,以便将来可以避免这种问题。提前谢谢!

3 个答案:

答案 0 :(得分:3)

不要使用原始指针,也不要在代码中的任何位置使用new。 Java的Cube[]的C ++等价物是std::vector<Cube>。您的示例函数可能如下所示:

std::vector<Cube> Cube::getChildren() const
{
    // 12 copies of current state
    std::vector<Cube> children(12, *this);

    for (int i = 0; i < children.size(); i++)
    {
        children[i].doMove(i);
    }

    return children;
}

在此之前还有其他更改可行(事实上,大量内存将被泄露,“副本”会相互影响)。


我猜你的CornerEdge构造函数的最后一个参数是某种方向指示符,当你的棋子旋转时你会改变它。所以变量WRGWGO应该是可变的,并编码该片段的当前方向。

在C ++中,对象应具有值语义。在这种情况下,这意味着复制对象应该执行“深层复制”,也就是说。一个克隆。除了在对象的构造函数内部之外,不应该有用于实现副本的代码。

因此,如果您的对象被设计为使用值语义,那么在children[i].pieces = pieces函数内尝试getChildren的问题将永远不会出现。

如果您的对象设计包含27个指向该类的可变成员的指针,则默认生成的复制构造函数将执行错误的操作,因为:

  • 所有“副本”实际上都有相同的指针 - 只有一个实际的一组。 (在新的立方体中重新定位片段将重新定位复制的立方体中的片段)
  • 即使修复了这个问题,“复制”也会指向原始多维数据集的各个部分,而不是复制的多维数据集。

所以,这在C ++中不是一个好的设计。

按价值按住件会更好。对原始代码的改进(但仍然不可行)将是:

Corner WRG {WHITE, RED, GREEN, WHITE};
Edge WR {WHITE, RED, WHITE};

Pieces *pieces[3][3][3] =
{ {&WRG, &WR, &WBR}, {&GR, nullptr, &BR}, // etc...

使用这个版本,至少没有内存泄漏,但是仍然存在这样的问题:默认生成的复制构造函数将使用旧多维数据集的Pieces复制新的多维数据集的Piece指针。


有三种方法可以解决这个问题:

  1. 编写一个拷贝构造函数(和一个拷贝赋值运算符),它检测旧多维数据集的指针指向哪一块,并使新多维数据集中的每个对应指针指向新块
  2. 将这些部分设为static,因此实际上只有一组。将使用单独的变量记住方向。
  3. 按值而不是按指针按住片段。
  4. 1 是您目前正在尝试做的事情,实际上比看起来更难;即使您使用std::copy或等效项修复了编译错误,您仍然会指向旧多维数据集的指针。

    2 是一个好主意。如果只有一组碎片,那么你可以将指针复制到碎片而不会造成任何麻烦。当然,您需要每个Cube都有一个新数组来表示每个部分的当前方向。这仍然比#1更简单!

    此选项还可以大大减少每个州的内存占用量。

    (有关#3和内存使用情况的更多评论,请参阅下文。)


    以下是策略2的实现方式。在EdgeCorner的定义中,取出与方向相对应的字段。

    class Cube
    {
        // "static" means only one copy of each for the whole program
        // The constructor arguments for each one are placed in the .cpp file
        static constexpr Corner WRG, WGO, WOB, /*....*/ ;
        static constexpr Edge WR, GR, /*.....*/ ;
    
        Pieces const *pieces[3][3][3] =
        { {&WRG, &WR, &WBR}, {&GR, nullptr, &BR}, // etc...
    
        typedef unsigned char Orientation;
    
        Orientation orientation[3][3][3] = { };
    
    public:
        // no default constructor needed if you got the above lists right
        // no destructor needed either way
        // Cube();  
    
        void rotateRedClock();
        void rotateRedCounter();
        // etc. - you'll probably find it easier to roll all of these into
        // a single function that takes the face and the direction as parameter
    
        void doMove(int);    // suggest using an enum to describe possible moves
    
        // not necessary getPieces();
    
        vector<Cube> getChildren() const;
    };
    

    如果您计划某种求解算法,则需要减少每个状态的内存占用量。所以 3 也是一个好主意。

    如果你采用#2,那么做#3就不那么紧迫了 - 你可以通过方法#2使你的代码工作并在以后优化它。

    要使用#3,您需要放弃使用Piece *多态的想法。

    但是你无论如何都不需要这个功能,因为你可以从数组索引中判断出这个部分应该是一个角还是一个边。我建议只使用Piece基本上是Corner现在但没有方向;当你期待边缘时忽略第三个字段。

    我的建议是用27个字节的表替换27个指针的表。然后,在您的.cpp文件中,您将拥有一个查找表,您可以使用该表来获取与该索引相对应的部分。

    检查我的帖子的编辑记录,以查看其外观的大致轮廓。 (我最初写过,但后来决定当前版本更容易消化!)

答案 1 :(得分:0)

在C ++中,您不能将另一个数组分配给固定长度的数组。您必须单独复制值。 (不是100%肯定,但非常确定。)

class Cube的定义中,你的意思是指向这样一个数组的指针,即你的意思是

typedef Piece PieceArray [3][3][3];
PieceArray * pieces;

在你的代码中,你没有声明一个指向数组的指针,而是指向一个指向数组的指针数组。

帮自己一个忙,并使用std::vector

答案 2 :(得分:0)

您没有提供Cube::pieces pieces的样子并且int[3][3][3]类型。问题是它是 C风格的数组和C非常弱的类型,所以C和C ++都无法区分int[3][3][3]int[2][2] - 类型信息丢失。

考虑使用 C ++ 数组类型 - 它们定义复制构造函数赋值运算符并在内部保存它们的大小,因此它们将完成所有工作您。让我们坐一趟!

C样式数组

正如我们已经知道的那样,这不起作用,这只是一个例子。

int main() {
    int i[2][2] = { {0, -1}, {1, 2} };
    int b[2][2];    
    b = i; /* cube.cpp:5:9: error: invalid array assignment */
}

std::vector

为此,您需要定义向量向量的向量:

#include <vector>

int main() {
    std::vector<std::vector<int> > i = { {0, -1}, {1, 2} };
    std::vector<std::vector<int> > b;    
    b = i;
}

boost::multi_array

(这将需要外部库)

#include <boost/multi_array.hpp>

int main() {
    boost::multi_array<int, 2> i(boost::extents[2][2]); 
    /* Note that for multi_array we need single-dimentional initializer */
    auto _i = { /*{*/ 0, -1 /*}*/, 
                /*{*/ 1, 2  /*}*/ };    
    i.assign(_i.begin(), _i.end());

    boost::multi_array<int, 2> b(boost::extents[2][2]);    
    b = i;
}

这似乎比其他解决方案更复杂,但multi_array可能比矢量矢量更有效。