Dies war eine Hausaufgabe aus dem letzten Jahr, die ich beschloss, aufzuschlagen und durchzuschauen. Ich habe den Debugger durchlaufen und festgestellt, dass der Destruktor am Ende einer Zuweisungsoperation aufgerufen wurde (überladene Zuweisung). Kann mir jemand sagen, warum es heißt? Ich habe den vollständigen Code bereitgestellt, damit er in einem Compiler ausgeführt werden kann (was die Beantwortung der Frage erleichtern kann), aber meine Frage betrifft nur einen sehr kleinen Teil des Codes. In der Treiberdatei gibt es eine Funktion namens void TestCopyConstructor()
. In dieser Funktion gibt es eine Zuweisungsoperation X = "0.12345";
. Dies ruft eine der überladenen Zuweisungsoperatorfunktionen MyFloat MyFloat::operator= (const char *Input)
auf. Diese Funktion läuft und am Ende davon heißt ein Kopierkonstruktor MyFloat MyFloat::MyFloat(const MyFloat & RHS)
. Nach dieser Funktion kehrt es zurück zu void TestCopyConstructor()
, wo der Destruktor aufgerufen wird. Ich versuche zu verstehen, warum dieser Destruktor aufgerufen wird. Ich weiß, dass das eine Menge Code ist, weshalb ich versucht habe, durch den bestimmten Bereich zu gehen, in dem ich verwirrt bin, da ich nicht sicher war, wie ich eine abgespeckte, funktionierende Version dieses Codes machen könnte.Versuchen zu verstehen, warum der Klassenzerstörer nach dem Zuweisungsoperator aufgerufen wird
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");
}
UND die Klasse selbst:
/******************************************************************
----------------------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;
}
@ πάνταῥεῖ Nein, tun sie nicht. Warum fragst du? Ich bin mir nicht sicher, ob ich mit dieser Frage genau dem folge, was Sie fahren. –
@ πάνταῥεῖ Muss ich? Färbe mich dicht, aber ich sehe nicht, wo ich sowas sage. Die Wörter "Konstruktor" oder "Retunn" erscheinen nie einmal in meiner Antwort. –
Diese Art von sinnvoll, indem Sie den Rückgabetyp sowohl die Prototyp- und Funktionsdefinition von MyFloat zu MyFloat & ändern, werden sowohl der Kopierkonstruktorfunktionsaufruf als auch der Destruktorfunktionsaufruf aus dem Prozess eliminiert. Obwohl, um ehrlich zu sein, ich bin immer noch ein wenig unklar, was zerstört wird (im ursprünglichen Code, der es wertmäßig zurückgibt). Eine Kopie des Objekts im Zuweisungsoperator? Oder die Kopie, die zurückgegeben wird? –