【问题标题】:BigInt C++ and proper copy constructor?BigInt C++ 和正确的复制构造函数?
【发布时间】:2015-11-06 02:43:11
【问题描述】:

我正在尝试为 C++ 实现 BigInt,但遇到了复制构造函数的问题。你可以看到我注释掉了复制构造函数的原始代码,它只是*this = orig;。但我发现你需要使用指针来代替。我不完全确定这完全是如何工作的,但是目前代码没有正确地创建一个复制构造函数。

-BigIntVector 是一个自定义向量类。与 STL 向量比较。

BigInt.h:

class BigInt {
private:
    BigIntVector bigIntVector;
    bool isPositive;
    int base;
    unsigned int skip;

    BigIntVector* ptr; //pointer to copy?

public:
    // copy constructor
    BigInt(BigInt const& orig);

    // constructor where data value is passed as a long
    BigInt(long num);

    // destructor
    ~BigInt();

    // binary '+' operator
    BigInt operator+(BigInt const& other) const;

    // unary '+' operator
    BigInt operator+() const;

    //more operator unloading functions

这是我当前在 BigInt.cpp 中的构造函数实现:

// copy constructor
BigInt::BigInt(BigInt const& orig) {
    ptr = new BigIntVector;
    *ptr = *orig.ptr;
    //*this = orig;
}

// constructor where operand is a long
BigInt::BigInt(long num) {
    //this->data = num;

    ptr = new BigIntVector;

    base = 10;

    int sizeOfLong = 0; //holds size of num
    int tempNum = num; 

    //get size of num
    if (tempNum == 0) {
        sizeOfLong = 1;
    }
    while (tempNum != 0)
    {
        tempNum /= 10;
        ++sizeOfLong;
    }

    //resize vector to match size of long
    bigIntVector = BigIntVector(sizeOfLong);

    if (num < 0) {
        isPositive = false;
        num *= -1;
    }
    else {
        isPositive = true;
    }
    long pushedNum;
    //cout << "num: " << num << endl;
    for (int i = sizeOfLong - 1; i >= 0; --i) {
        pushedNum = (long)(num%base);
        bigIntVector.setElementAt(i, pushedNum);
        num /= base;
    }
}

// destructor
BigInt::~BigInt() {
    //delete *this;
}

//code for overloading operators for BigInt below

BigIntVector 构造函数的代码:

BigIntVector::BigIntVector(long initialSize)
{
    vectorTotalSize = initialSize;
    vectorIncrementSize = initialSize;

    vectorArray = (long *)malloc(initialSize*sizeof(long));
    for (long i = 0; i < initialSize; i++) vectorArray[i] = 0;

    nextValue = 0;
}

【问题讨论】:

  • *this = orig; 无论如何都不是实现复制构造函数的正确方法。赋值运算符应该利用复制构造函数,而不是相反。
  • 更新:“-BigIntVector 是一个自定义向量类。与 STL Vector 比较。”
  • 类定义中的BigIntVector* ptr; 是什么?您的构造函数使其指向 new 向量,但随后没有在这个新向量中放入任何内容。
  • 除特殊情况外,不要new数据结构对象本身,如ptr = new BigIntVector。而是让他们成为正式成员。您可能会发现默认生成的复制构造函数和赋值运算符会自动为您工作。
  • 我认为这是成为 Java 大用户的副作用。 “正式成员”的正确语法是什么

标签: c++ pointers constructor copy-constructor biginteger


【解决方案1】:

在现实世界中(家庭作业之外),BigInt 类不需要显式的复制构造函数。内存分配应该委托给一个单独的类——很可能是std::vector。如果您需要一个指针作为类成员(在这种情况下不太可能),使用 std::shared_ptr 管理它就不需要复制构造函数。

这是我较早的一篇文章,解决了您对在 C++ 中使用 new 的误解。该描述中对 C# 的引用同样适用于 Java:Is garbage collection automatic in standard C++?

关于 cmets 中的一个问题:“正式成员的语法是什么”。暗示这只是说不要将元素声明为指针(只需省略*)。总而言之,对于这个类:

  • 您不需要指针
  • 您不需要复制构造函数
  • 您不需要使用new 关键字

在这种情况下,问题在于您的BigIntVector 类。由于这是分配内存和管理指针的部分,因此需要复制构造函数。如果这是一项家庭作业,我建议将rule of three 应用于您的BigIntVector 实现。您将需要所有以下各项:

  • 复制构造函数,用于复制内存
  • 赋值运算符,它也复制内存
  • 析构函数,释放内存

如果不是功课,我建议把BigIntVector换成std::vector


为非平凡类声明复制构造函数是个坏主意,因为您通常需要显式复制每个成员(或者它们将被默认初始化而不是复制)——最好让编译器来做为您避免错误。

如果你必须为这个作业准备一个,正确的形式应该是这样的:

BigInt::BigInt(BigInt const& orig)
 : bigIntVector(orig.bigIntVector)
 , isPositive(orig.isPositive)
 , base(orig.base)
 , skip(orig.skip)
{
// empty body
}

将算术运算符转发到复合赋值运算符的规范形式是:

T& T::operator += ( T const & b )
{
  ...class-specific math logic that modifies the object...
  return (*this);
}

T operator + ( T const & a, T const & b )
{
  T temp(a); // create a temporary copy of 'a' rather than modifying it
  return temp += b;
}

【讨论】:

  • 每当我重用 BigInt 类的新实例时,它都会尝试重用最后一个实例。例如,num1 = 10 和 num2 = 5 和 num3 = num1 + num2 和 num4 = ++num1。 Num4 会高得多,因为出于某种原因它被添加到 num3 中。也许它与复制构造函数有关,这就是为什么在这种情况下它是必要的。
  • 谁编写了BigIntVector 类?它有自己的复制语义——你需要理解。 std::vector 在您复制或分配它时会复制其内容,但BigIntVector 可能会共享其内容(更像是Java 中的标准方法)。如果是这种情况,那么您可能确实需要一个指针来使您的类按照您想要的方式运行,但最好使用smart pointer,例如std::shared_ptr,除非那不是任务允许。如果使用智能指针,则不需要复制构造函数。
  • 大部分都是我写的。我用我的 BigIntVector 构造函数更新了这篇文章。我相信复制时会执行共享内容。我需要一个复制构造函数,但我需要帮助弄清楚如何设置指针。
  • 感谢 BigIntVector 的建议。我还需要在 BigInt 中有复制构造函数,你有建议的实现吗?
  • 你帮了大忙!谢谢!我还有一个问题。如果我想在重载的 += 运算符函数中访问 BigInt 的复制版本,我该怎么做。例如,说 num3 = num1 + num2 所以我不想将 num1 的实际实例更改为 += 中的新数字(因为 + 重定向到 +=),我希望 num1 的副本更改为新数字.我希望这是有道理的。再次感谢!
【解决方案2】:

嗯。我不知道BigIntVector* ptr; //pointer to copy? 是做什么用的,所以我假设这是一个错误。您的代码应如下所示:

class BigInt
{
private:
    BigIntVector bigIntVector;
    bool isPositive;
    int base;
    unsigned int skip;

public:
    // constructor where data value is passed as a long
    BigInt(long num = 0);

    BigInt &operator+=(BigInt const& other);

    // Other accessors...
};

inline BigInt operator+(BigInt a, BigInt const &b) { a += b; return a; }

这里的主要原则是Rule of Zero。您设计您的类,以便默认生成的函数对以下内容执行正确的操作:复制构造函数、移动构造函数、移动赋值、复制赋值、析构函数。

那么你的类定义仍然很简单。为了实现这个目标,所有数据成员都必须正确地实现这些功能——要么自己遵循零规则,要么实际拥有这些功能。

内置类型都遵循规则,我假设 BigIntVector 遵循 0,3 或 5 规则。(如果不是,请修复它!)

我所做的其他更改是:

  • 将非可变运算符作为非成员函数是一种很好的风格。如果您发现自己能够将const 放在成员运算符上,则表明它应该是非成员是一个好兆头。有关此主题的更多信息,请参阅this thread
  • 注释应该说明代码内容之外的内容。将注释“析构函数”放在析构函数上,或“二进制 + 运算符”放在二进制 + 运算符上只会无缘无故地弄乱你的屏幕。

在您的long 构造函数中,int tempNum = num; 应该是long tempNum = num;;你应该初始化skip

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-13
    • 2023-03-31
    • 2013-04-29
    相关资源
    最近更新 更多