试图理解为什么在赋值运算符之后调用 class 析构函数

Trying to understand why class destructor is being called after assignment operator

这是去年的家庭作业,我决定打开并浏览一下。我 运行 通过调试器注意到析构函数在赋值操作结束时被调用(重载赋值)。谁能告诉我为什么叫它?我提供了完整的代码,以便它可以在编译器中 运行 (这可能更容易回答问题),但我的问题只是关于代码的一小部分。

在 driver 文件中,有一个名为 void TestCopyConstructor() 的函数。在这个函数中,有一个赋值操作X = "0.12345";。这将调用重载赋值运算符函数之一 MyFloat MyFloat::operator= (const char *Input)。这个函数运行,最后,一个复制构造函数被称为 MyFloat MyFloat::MyFloat(const MyFloat & RHS)。在这个函数之后,它 returns 回到 void TestCopyConstructor() ,在那里调用了析构函数。我试图理解为什么调用这个析构函数。我意识到这是很多代码,这就是为什么我试图遍历我感到困惑的特定区域,因为我不确定如何制作这段代码的精简工作版本。

DRIVER:

#include "MyFloatD.cpp"   //  Name of your class definition. Use pathname
//    if file is not in current working directory.
#include <iostream>
#include <iomanip>
#include <ctype.h>
#include <new>
using namespace std;

void DisplayTestingOptions();
void GetChoice(char& Ch);

void TestInputOperator();
void TestAssignment();
void TestCopyConstructor();
void TestComparison();
void TestPlus();
void TestDestructor();

float CPU_Seconds();

//==============================  main   =================================

int main()
{
    char    Choice;

    do
    {

        DisplayTestingOptions();

        GetChoice(Choice);

        switch ( Choice )
        {
            case '1': TestCopyConstructor();
                break;
            case '2': TestAssignment();
                break;
            case '3': TestInputOperator();
                break;
            case '4': TestPlus();
                break;
            case '5': TestComparison();
                break;
            case '6': TestDestructor();
                break;
            case 'Q':      ;                           // Exit
        };
    }
    while ( Choice != 'Q' );

    return 0;
}

/***********************  SpaceBarToContinue    **************************

 Displays "Spacebar to continue" and returns 1 iff Spacebar pressed.

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
int SpaceBarToContinue(const char* Message = "  Spacebar continues this test")
{
    char Ch;

    cout << "\n------------------------------------------"
    <<   Message;
    Ch = cin.get();

    return ( Ch == ' ' );
}

/***********************  DisplayTestingOptions   ************************

 Displays a menu of choices.

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void DisplayTestingOptions()
{
    const char* TAB = "     ";  // Provides menu spacing

    cout << "\n\n";
    cout << "\n----------------------------------"
    "------------------------------------\n\n";

    cout << TAB << "1)  Test copy constructor     ";

    cout << TAB << "2)  Test assignment operator \n";

    cout << TAB << "3)  Test >> input function    ";

    cout << TAB << "4)  Test + operator       \n";

    cout << TAB << "5)  Test == operator          ";

    cout << TAB << "6)  Test destructor       \n";

    cout << TAB << "Q)  Quit program\n\n";

    cout << "Choice? ";

}

/***********************  GetChoice   ************************************

 Reads a char from the keyboard, provides a redirectable echo and
 upcases the char.

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void GetChoice(char& Ch)
{
    cin >> Ch;
    Ch = toupper(Ch);

}

/*********************  TestInputOperator   ******************************

 Allows testing of the member functions ">>" of MyFloat.

 The insertion operator << is assumed to be correct.

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void TestInputOperator()
{
    MyFloat X;

    cout << "\n\n>>>>>>>>>>>>>  Testing input operator >>>>>>>>>>>>>>>>>\n";
    do
    {
        cout << "\nEnter MyFloat ==> ";
        cin  >> X;

        cout << "\nAfter the read,  X = '" << X << "'\n\n";
    }
    while ( SpaceBarToContinue() );

    if (cin.peek() != EOF)
        cin.ignore(1000, '\n');

}


/*********************  TestAssignment   *********************************

 Allows testing of the member function "=" of MyFloat. If a deep copy
 assignment operator has not been written, the code below will give
 unexpected output.

 This routine assumes that input and output functions for MyFloats
 have been written and debugged.

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void TestAssignment()
{
    MyFloat X(10), Y(10);

    cout << "\n\n------------  Testing '='  for MyFloats   --------------------\n\n";

    X = "0.1234567890";

    Y = X;                // This must be a deep copy

    X = "0.0";            // Or this will change Y!

    cout << "\nAfter the assignments, X = \"0.1234567890\", Y = X, and X = 0.0, "
    << "Y = " << Y << endl;

}


/*********************  TestPlus   ***************************************

 Allows testing of the member function operator+. At the present time,
 it allows testing only of addition for MyFloats that have the default
 length.

 NOTE:  Calls two of the copy constructors. The copy constructor also
 needs to be working correctly for + to work.

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void TestPlus()
{
    MyFloat X(12), Y, Sum;


    cout << "\n\n++++++++++++++++  Testing \"+\"  for MyFloat  ++++++++++++++++\n"
    << "                  (Do not use leading zero)\n";
    do
    {
        cout << "\nEnter X  ==>   ";
        cin >> X;
        cin.ignore(1000, '\n');         //  Discard all chars in input stream.

        cout << "\nEnter Y  ==> + ";
        cin >> Y;

        cin.ignore(1000, '\n');         //  Discard all chars in input stream.
        cout << "             ";

        for ( int k = 1; k <= X.Digits() + 3 || k <= Y.Digits() + 3 ; ++k )
            cout << '-';

        cout << "\n              ";

        Sum = X + Y;

        cout << Sum << "\n\n";

    }
    while ( SpaceBarToContinue() );
}


/*********************  TestCopyConstructor   ****************************

 Allows testing of the copy constructor that is called automatically
 under certain circumstances.

 Note that that the default constructor and the overloaded "=" operator
 must also be working properly.

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void TestCopyConstructor()
{
    MyFloat X;      // Default constructor called

    X = "0.12345";  // Overloaded "=" operator called

    MyFloat Y = X;  // Copy constructor called

    X = "0.0";

    cout << "\n\n============ Testing copy constructor  ================\n\n";
    cout << "NOTE: This function also calls default constructor and '='operator. \n\n\n";

    cout << "After 'X = 0.0',  Y = " << Y << "\nNote that Y should be 0.12345\n";

}

/*********************  TestComparison   *********************************

 Allows testing of the member function operator+. At the present time,
 it allows testing only of addition for MyFloats that have the default
 length.

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void TestComparison()
{
    MyFloat A, B, Sum;


    cout << "\n\n== == == == ==  Testing \"== \" for MyFloat  == == == == == \n\n";

    cout << "MyFloat variables have maximum length of " << A.MaxDigits() << endl;
    do
    {
        cout << "\nEnter A  ==> ";
        cin  >> A;
        cout << "\nEnter B  ==> ";
        cin  >>  B;

        cout << "\n (A == B) is " << ((A == B) ? "TRUE " : "FALSE ") << endl;


    }
    while ( SpaceBarToContinue() );
}

/*********************  TestDestructor  **********************************

 Tests to see if the class destructor has been correctly written. If not
 this program may crash.

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void TestDestructor()
{

    const unsigned long REPETITIONS = 70000;

    for ( unsigned long N = 1;  N <= REPETITIONS;  ++N )
    {
        MyFloat X(10000);  // Create 1000 digit MyFloat
    }                 // X goes out of scope at each repetition

    // If the destructor is not correct, each repetition of the
    // loop above will caused some memory leak and the call to
    // new below will fail.

    int* A = new int[120];

    if ( A == NULL )
        cout << "\n\nYour memory has leaked away!\n";
    else
    {
        delete [] A;
        cout << "\n\n~~~~~~~~~~~~~~  destructor is OK!  ~~~~~~~~~~~~~~~\n";
    }
    SpaceBarToContinue(" spacebar to continue");
}

和CLASS本身:

/******************************************************************
----------------------Class Breakdown----------------------------

 Data Members:

 -enum (Default Size) sets the size of the MyFloat to 10 by default if no
            size is specified

 -Char *Number points at array being dynamically allocated in memory

 -NumberOfDigits represents the length of the current float

 -MaxNumberOfDigits represents the longest length that that particular 
            object can hold

 Member Functions:

 -MyFloat(const MyFloat & RHS) copy constructor allows usuer to initialize
        new object with another object, by placing it in the parameter list

 -MyFloat() is default constructor, initializes objects at MaxSize of 10

 -MyFloat(unsigned int Input) standard constructor allows user to create
        MyFloat of any size by placing a numerical value in the parameter list

 -int Digits() returns size of number currently stored in object

 -int MaxDigits() returns the maximum size allowed in current object

 -MyFloat operator= (const char *Input) overloaded assignemnt operator
        to work with setting an object equal to a literal string (X="0.004")

 -MyFloat operator= (const MyFloat &X) overloaded assignent operator to work 
        with setting an object equal to another object

 -int operator== (const MyFloat &x) overloaded comparison operator

 -MyFloat operator+ (MyFloat X) overloaded addition operator

 -int operator> (MyFloat x) overloaded greater than comparison operator

 -friend ostream& operator<< (ostream &Out, const MyFloat & X)
        overloaded insertion operator, so user can cout << X

 -friend istream& operator>> (istream &In, MyFloat & X)
        overloaded extraction operator, so user can cin >> X
 ********************************************************************/
#include <iostream>
#include <ctype.h>

using namespace std;

class MyFloat
{
    enum {DefaultSizeTen=10};
    char *Number;

    int NumberOfDigits;
    int MaxNumberOfDigits;

public:

    ~MyFloat();

    MyFloat(const MyFloat & RHS);
    MyFloat();
    MyFloat(unsigned int Input);

    int Digits();
    int MaxDigits();

    MyFloat operator= (const char *Input);
    MyFloat operator= (const MyFloat &X);

    int operator== (const MyFloat &x);

    MyFloat operator+ (const MyFloat &X);
    int operator> (const MyFloat &x);

    friend ostream& operator<< (ostream &Out, const MyFloat & X);
    friend istream& operator>> (istream &In, MyFloat & X);

};

/********************* MyFloat Destructor ***************************
 Action: Deletes the memory being pointed to by the pointer Number;
 Parameters:
    IN: none
    OUT: none
 Returns: none
 Preconditions: Number has to be pointing at memory, or else error
        occur
 *******************************************************************/

MyFloat::~MyFloat()
{
    delete Number;
}

/********************* MyFloat Copy Constructor *********************
 Action: Initializes an object of MyFloat to be equal to any object being
        passed in via parameter list
 Parameters:
    IN: none
    OUT: const MyFloat & RHS - float being getting copied
 Returns: none
 Preconditions: must be of same data type - MyFloat
 *******************************************************************/

MyFloat::MyFloat(const MyFloat & RHS)
{
    MaxNumberOfDigits=RHS.MaxNumberOfDigits;
    NumberOfDigits=RHS.NumberOfDigits;

    Number = new (nothrow) char[RHS.NumberOfDigits+1]; //+1 for overflow

    if (Number != NULL)
    {
        for (int i=0; i<=RHS.NumberOfDigits-1; ++i)
        {
            Number[i]=RHS.Number[i];
        }
    }

    else
        NumberOfDigits=0;
}

/********************* MyFloat Default Constructor *******************
 Action: Sets NumberOfDigits to 10 and places 0 in all elements of array
        being pointed to by *Number
 Parameters:
    IN: none
    OUT: none
 Returns: none
 Preconditions: none
 *******************************************************************/

MyFloat::MyFloat()
{
    MaxNumberOfDigits=DefaultSizeTen;//set MaxNumberOfDigits to default size of 10
    NumberOfDigits = 0;

    Number = new (nothrow) char[MaxNumberOfDigits+1]; //+1 for overflow

    if (Number != NULL)
    {
        for (int i=0; i<=MaxNumberOfDigits; ++i)
        {
            Number[i]=0;
        }
    }
}

/********************* MyFloat Standard Constructor *******************
 Action: Sets NumberOfDigits to be equal to whatever user places inside of
        the paramter list
 Parameters:
    IN: unsigned int Input
    OUT: none
 Returns: none
 Preconditions: Input must be int and not negative
 *******************************************************************/

MyFloat::MyFloat(unsigned int Input)//Constructor that zeros out Number[] and NumberOfDigits
{
    MaxNumberOfDigits=Input;
    NumberOfDigits=0;

    Number = new (nothrow) char[MaxNumberOfDigits+1];

    if (Number != NULL)
    {
        for (int i=0; i<=MaxNumberOfDigits; ++i)
        {
            Number[i]=0;
        }
    }
}

/********************* Digits() ********************************
 Action: Simply returns NumberOfDigits to main
 Parameters:
    IN: none
    OUT: none
 Returns: returns the NumberOfDigits
 Preconditions: none
 *******************************************************************/

int MyFloat::Digits()
{
    return NumberOfDigits;
}

/********************* MaxDigits() ********************************
 Action: Returns MAXDIGIT to main
 Parameters:
    IN: none
    OUT: none
 Returns: returns MAXDIGIT
 Preconditions: none
 *******************************************************************/

int MyFloat::MaxDigits()//Return MAXDIGIT or MaxNumberOfDigits?
{
    return MaxNumberOfDigits;
}

/********************* operator=() ********************************
 Action: Overloads the assignment operator to allow a string to be stored
    in the MyFloat data type, by dynamically allocating memory for a new
    array
 Parameters:
    IN: none
    OUT: Const char *Input, which points to the string being assigned to MyFloat
        data type
 Returns: *this

 Preconditions: Assumes incoming string was typed with leading '0' and '.',
    these are skipped when storing the string in the MyFloat data type
    Number isn't stored if decimal isn't present

 *Modified overloaded = function from solution set, since my original 
 function had some errors
 *******************************************************************/

MyFloat MyFloat::operator= (const char *Input)
{
    int CounterForInput = 0, CounterForNumber = 0, Length = 0, Temp = 0;

    NumberOfDigits = 0;


    /* SKIP PAST LEADING WHITESPACE, LEADING 0s, and Decimal */


    while ( (isspace(Input[CounterForInput]) || Input[CounterForInput] == '0') && Input[CounterForInput] != 0  )    //  Skip blanks and zeros
        ++CounterForInput;

    if ( Input[CounterForInput] != '.' )
        return *this;                 // Error, no decimal point

    ++CounterForInput;                            // Move to char after '.'


    /* CHECK TO SEE IF POINTER IS POINTING AT AN ARRAY WITH ENOUGH ROOM FOR COPY */


    Temp = CounterForInput;

    while (isdigit(Input[Temp])) // get length of incoming float without affecting CounterForInput
    {
        ++Length;
        ++Temp;
    }

    if (MaxNumberOfDigits<Length)
    {
        delete Number;
        Number = new (nothrow) char[Length+1];

        if (Number == NULL)
        {
            NumberOfDigits = 0;
            return *this;
        }
    }


    /* ASSIGN INPUT TO ARRAY BEING POINTED AT BY NUMBER */


    while ( CounterForNumber <= MaxNumberOfDigits && isdigit(Input[CounterForInput]) ) //  Copy rest of string
        Number[CounterForNumber++] = Input[CounterForInput++] - '0';

    NumberOfDigits = CounterForNumber;

    while ( CounterForNumber < MaxNumberOfDigits )       // Pad with trailing zeros
        Number[++CounterForNumber] = 0;

    return *this;
}

/********************* operator=() ********************************
 Action: Another Overloaded assignment operator to allow a MyFloat to be
    copied to another MyFloat, by dynamically allocating memory for a new
    array
 Parameters:
    IN: none
    OUT: Const MyFloat &X
 Returns: *this
 Preconditions: none
 *******************************************************************/

MyFloat MyFloat::operator= (const MyFloat &X)
{
    if (MaxNumberOfDigits<X.MaxNumberOfDigits)
    {
        delete Number;

        Number = new (nothrow) char [X.MaxNumberOfDigits+1];

        if (Number == NULL)
        {
            NumberOfDigits=0;
            return *this;
        }
    }

    NumberOfDigits = X.NumberOfDigits;
    MaxNumberOfDigits = X.MaxNumberOfDigits;

    for (int i=0; i<=MaxNumberOfDigits; i++)
    {
        Number[i] = X.Number[i];
    }

    return *this;
}

/********************* operator==() ********************************
 Action: Overloads the comparison operator to compare data members of
        two different objects of MyFloat
 Parameters:
    IN: none
    OUT: const MyFloat &x, which contains data members holding one float
 Returns: 1 if both MyFloat numbers are equal, 0 if they are different
 Preconditions: none
 *******************************************************************/

int MyFloat::operator== (const MyFloat &x)
{
    int Counter;

    if (NumberOfDigits>x.NumberOfDigits)
        Counter = NumberOfDigits;

    else
        Counter = x.NumberOfDigits;

    for (int i=0; i<=Counter || Number[i]; ++i)
    {
        if (x.Number[i]!=Number[i])
            return 0;
    }
    return 1;
}

/********************* operator+() ********************************
 Action: Overloads the addition operator to allow it to add two MyFloats
        together
 Parameters:
    IN: none
    OUT: MyFloat x, which contains data members holding one float
 Returns: MyFloat Storage back to main driver
 Preconditions: none
 *******************************************************************/

MyFloat MyFloat::operator+ (const MyFloat &X)
{
    int Carry=0;
    int Integer=0;
    int DifferenceInLength=0;
    int ForLoopCounter=0;
    MyFloat Storage;

    if (NumberOfDigits > X.NumberOfDigits)
    {
        Storage = *this;
        //just copy larger float over to storage so it is large enough to hold new number
        //new number will use the same ending numbers as the longest number as well

        DifferenceInLength = NumberOfDigits - X.NumberOfDigits;
        //Get the difference between the lengths of the two numbers, will be used in next for loop

        ForLoopCounter=NumberOfDigits - DifferenceInLength;
    }

    else
    {
        Storage = X;
        DifferenceInLength = X.NumberOfDigits - NumberOfDigits;
        ForLoopCounter=X.NumberOfDigits - DifferenceInLength;
    }

    for (int i=ForLoopCounter-1; i>=0; --i) //- 1 to work with Array style counting
    {
        Integer = ((X.Number[i]) + (Number[i]) + (Carry));

        Carry = 0;

        if (Integer>=10)
        {
            Carry = Integer/10;
            Integer %= 10;
        }

        Storage.Number[i] = Integer;

        Integer = 0;
    }

    if (NumberOfDigits>X.NumberOfDigits)
        Storage.NumberOfDigits = (NumberOfDigits);

    else
        Storage.NumberOfDigits = X.NumberOfDigits;//Storage.NumberOfDigits will be equal to the larger NumberOfDigits of the two floats being added

    return Storage;
}

/********************* operator>() ********************************
 Action: Overloads the greater than operator to allow two MyFloats to be
    compared
 Parameters:
    IN: none
    OUT: const MyFloat &x, which contains data members holding one float
 Returns: 0 if false, 1 if true (if calling object is greater than object
    being passed in)

 Preconditions: none
 *******************************************************************/

int MyFloat::operator> (const MyFloat &x)
{
    for (int i=0; i<=MaxNumberOfDigits; ++i)
    {
        if (Number[i]<x.Number[i])
            return 0;
        else if (Number[i]>x.Number[i])
            return 1;
    }
    return 1;
}

/********************* operator<<() ********************************
 Action: Overloads << operator to allow it to work on custom MyFloat
        data type
 Parameters:
    IN: none
    OUT: ostream &Out, const MyFloat & x
 Returns: Reference to ostream
 Preconditions: none
 *******************************************************************/

ostream & operator<< (ostream &Out, const MyFloat & X)
{
    Out << "0.";

    if (X.NumberOfDigits != 0)
    {
        for (int j=0; j<=X.NumberOfDigits-1; ++j)
        {
            Out << (int) X.Number[j];
        }
    }

    else
        Out << "?";

    return Out;
}

/********************* operator>>() ********************************
 Action: Reads and stores the float typed in by the user using certain
    conditions.  Skips all leading whitespace and 0s, then stores any
    numbers after that.
 Parameters:
    IN: none
    OUT: istream &In, Myfloat & x
 Returns: reference to istream
 Preconditions: none
 *******************************************************************/

istream & operator>> (istream &In, MyFloat & X)
{
    int Counter=0;
    char Character;

    cin.ignore(); // clear newline from input buffer, my algorithm doesn't
                  // work if newline is in input buffer when entering function

    X.NumberOfDigits=0;

    cin.get(Character);

    while ((isspace(Character) || Character=='0') && (Character != '\n'))
        cin.get(Character);

    if (Character != '.')
        return In;

    cin.get(Character);

    while (isdigit(Character) && Counter<=X.MaxNumberOfDigits-1)
    {
        X.Number[Counter]= Character-'0';
        cin.get(Character);
        ++Counter;
    }

    cin.putback(Character);//puts back last character stored - newline in this case

    X.NumberOfDigits= Counter;

    for (; Counter<=X.MaxNumberOfDigits-1; ++Counter)
    {
        X.Number[Counter]=0;
    }

    cin.ignore(100, '\n');  // flush any extra numbers in input - mostly for when
                            // user types in a number longer than array can hold

    return In;
}

class C returns C& 的典型赋值运算符 - 对赋值左侧对象的引用。

您的 returns 该对象的 副本,按值计算。然后立即销毁的是那个临时副本。