Scenario
I am in the process of learning C++, so please forgive my naivety. I have attempted to build my own class, objects and methods - each of which seem to work as expected. However I am running into issues with what seems to be uninitialized storage (and possibly the use of local objects?) however I would like to know how to fix it, if indeed it is meant to be, or an alternative. My current train of thought is that the object needs to be passed...however that could be way off...
Code
//header
class Car{
public:
USHORT GetMin();
void SetMin(USHORT min);
private:
USHORT itsMinPrice;
};
USHORT Car::GetMin(){
return itsMinPrice;
}
void Car::SetMin(USHORT min){
itsMinPrice = min;
}
-
void StartingPrices(){
Car Mercedes;
std::cout << Mercedes.GetMin() << "\n";
Mercedes.SetMin(50);
std::cout << Mercedes.GetMin()<< "\n";
}
int main(){
float input;
Car Mercedes;
Mercedes.SetMin(100);
StartingPrices();
std::cout << Mercedes.GetMin() << "\n";
std::cin >> input;
return 0;
}
Expected output
100, 50, 50
Actual output
debug win32 - 52428, 50, 100
release win32 - 0, 50, 100
In your StartingPrices function, the Mercedes object you call GetMin is created on the line before, i.e., not the same object as the one you create in the main function.
That means that the object do not yet have itsMinPrice set to anything, hence the value will be garbage (that is, a value which you don't really have control over), in this case 52428 in debug and 0 in release.
What I think you wish to do is pass a reference of the Mercedes object from the main function into the StartingPrices function:
void StartingPrices(Car& mercedes){
std::cout << Mercedes.GetMin() << "\n"; // Here the GetMin method will return 100.
...
}
int main(){
....
Car Mercedes;
Mercedes.SetMin(100);
StartingPrices(Mercedes);
....
}
Its also a good idea to set the default value of the members in the constructor of the class.
In your Car class you do not initialize your member variable itsMinPrice except when you call SetMin, this means there is a risk that you will use an uninitialized Car instance if you forget to call SetMin on it. Normally it is good to have initialization in a constructor of the class with some value e.g.
Car() : itsMinPrice(0) {
}
or create a constructor that takes an initial value
Car(USHORT minValue) : itsMinPrice(minValue) {
}
Related
I had this question on a test about a month ago and I still can't seem to understand it completely and quite frankly it's driving me crazy. I will include the question at the bottom. But, it's asking to create a single parameter constructor that creates a new "Vector" (the name of the class) which is the sum of two others. The vector class I made has a function set/get x and set/get y. My hang up is I can't seem to figure out how to make a function that adds the two x's and y's together from vector and vector1 to create a new Vector...call it vector2. I'll include everything I got so far. Thanks to anyone willing to make it through the wall of text as confusing as it must be haha.
Write a class Vertor with the following properties and place the class in a separate header file :
Add member function with a single parameter of another vector and returns a new vector that is the sum of the two (to add vectors you sum the components, for example, Cx = Ax + Bx and Cy = Ay + By).
Write a program that includes the Vector header file, constructs two different vectors and demonstrates the magnitude, angle, and add functions.
Data Members
vector
x component
y component
Member Functions
Set and Get functions for all data members
Magnitude member function
Angle member function (angle = inverse tangent(y / x))
ps I hope I am not doing anything wrong by uploading this and asking I have waited this entire time because I didn't want to break some sort of rule in the community....that I am honestly desperate to become a part of. I've dreamed of doing this my whole life and finally....ahh i digress sorry thanks guys
Oh...my code
#include "Vertor.h"
#include <iostream>
int main()
{
// creates a vector class
Vector vector;
vector.setXcom(4); // sets X
vector.setYcom(12); // sets Y
Vector vector1; // Creates another vector
vector1.setXcom(3);
vector1.setYcom(52);
Vector vector2; // constructs another vector that returns the sum of two other vectors
cout << vector.getXcom() << endl;
cout << vector.getYcom() << endl;
cout << vector.getMag() << endl;
cout << vector.getAng() << endl;
cout << vector1.getXcom() << endl;
cout << vector1.getYcom() << endl;
cout << vector1.getMag() << endl;
cout << vector1.getAng() << endl;
}
#include<iostream>
using namespace std;
// initalize variables
double xcomponent, ycomponent;
double ans, anns, annns;
class Vector // creates Vector class
{
public:
void setXcom(double x) // setX function
{
xcomponent = x;
}
void setYcom(double y) // setY function
{
ycomponent = y;
}
double getXcom() // getX function
{
return xcomponent;
}
double getYcom() // getY function
{
return ycomponent;
}
double getMag() // get magnitude function
{
double ans = sqrt((xcomponent * xcomponent) + (ycomponent * ycomponent));
return ans;
}
double getAng() // get angle function
{
double annns = atan(xcomponent / ycomponent);
return annns;
}
// setnewvec function to make a new vector from two others
void setNewVec(int a, int b)
{
xcomponent = a;
ycomponent = b;
}
// NOT SURE
Vector getNewVec(int a, int b)
{
return a + a;
return b + b;
}
};
So you have an absolutely fundamental misunderstanding or gap in your knowledge about how objects work, and this task will be impossible until you sort that out.
To illustrate here's a simpler example written in the style of your code above. I'll follow that with the same example written as it should be. This example is a simple Person class which has an age 'component'.
int age;
class Person
{
public:
void setAge(int a) { age = a; }
int getAge() { return age; }
};
int main()
{
Person fred;
fred.setAge(22);
Person mary;
mary.setAge(33);
cout << "Fred is " << fred.getAge() << " and Mary is " << mary.getAge() << endl;
}
If you run this program the output will be Fred is 33 and Mary is 33. Both the people have the same age even though you set them as different in the program.
The problem is that although this program has two people it only has one age. So it's literally impossible for the two people to have different ages.
Here's the program written correctly. The crucial difference is that the age variable is inside the class. This means that each Person object gets it's own age.
class Person
{
public:
void setAge(int a) { age = a; }
int getAge() { return age; }
private:
int age;
};
int main()
{
Person fred;
fred.setAge(22);
Person mary;
mary.setAge(33);
cout << "Fred is " << fred.getAge() << " and Mary is " << mary.getAge() << endl;
}
Now the output is Fred is 22 and Mary is 33 as it should be.
First thing you need to do, is to move xcomponent and ycomponent to inside the object. Right now they are global variables which means they share values in all objects you create (and outside object too).
I'm gonna assume you've learned about structures before moving to objects. It's pretty hard to understand object without knowing structures first.
Structures and classes are very similar. They both are containers for variables. Classes are a little more advanced version that usually hides the raw data and instead provides member functions (sometimes called methods) that allow to manipulate the data inside in a more convenient way.
Anyway, when you create a new object of a class, you create it with a new copy all member variables (fields) inside. This way, they can have different values for each object.
Your code is pretty easy to fix in that regard. Just move definition of these variables inside your class.
Old code:
double xcomponent, ycomponent;
double ans, anns, annns;
class Vector // creates Vector class
{
public:
//...
};
New code:
class Vector // creates Vector class
{
double xcomponent, ycomponent;
double ans, anns, annns;
public:
//...
};
Now we can work on the return value.
Your return value of getNewVec is all right. You've declared that you want to return an object of type Vector and this is exactly what you want.
However, the function should also take a single vector as an argument. Right now you have tho arguments int a and int b, none of which is a Vector. We need to change that to Vector otherVector to do what your assignment said.
The call of the function looks like this: someVector.getNewVec(someOtherVector).
When it runs, you have two vectors accessible inside of it. The first of them is the one on which the function was called. You have direct access to its fields. The second one is of course the argument otherVector. You can access its fields through its member functions. (Or you can access directly its private fields because you're in a member function of its class.)
Now you need to construct the new vector.
The simplest way is to just create it and assign the values one by one:
Vector getNewVec(Vector otherVector)
{
Vector newVector;
newVector.setXcom(xcomponent + otherVector.getXcom());
newVector.setYcom(ycomponent + otherVector.getYcom());
return newVector;
}
or:
Vector getNewVec(Vector otherVector)
{
Vector newVector;
newVector.setXcom(xcomponent + otherVector.xcomponent);
newVector.setYcom(ycomponent + otherVector.ycomponent);
return newVector;
}
or if you really want:
Vector getNewVec(Vector otherVector)
{
Vector newVector;
newVector.setXcom(this->getXcom() + otherVector.getXcom());
newVector.setYcom(this->getYcom() + otherVector.getYcom());
return newVector;
}
(this is a pointer the object your inside of. You have access to it from each member function.)
I recommend the second option.
Some additional stuff you can read about if your interested...
(I'm not gonna go into any details here.)
Constructors
You can have a special member function that is called when object it's created that is supposed to set initial values to the fields.
It is written similar to a function, except is doesn't have a return value and it's name is always the same as the name of the class.
Vector(int x, int y)
{
xcomponent = x;
ycomponent = y;
}
That allows to create an abject and assign the values in one line so instead of:
Vector newVector;
newVector.setXcom(12);
newVector.setYcom(42);
you can have:
Vector newVector(12, 42);
You can have more than one constructor with different list of arguments.
You can create an operator instead of a normal function. An operator is a function with specific name and arguments that can be called similarly to built-in mathematical operations.
Operator for addition looks like this:
Vector operator+(Vector otherVector)
//the body is the same as getNewVec
You could call it like a normal member function:
someVector.operator+(someOtherVector)
but a better way of writing it is:
someVector + someOtherVector
So my program is intended to accept input from the user to create an object with several attributes(variables) and those objects are put into vector. I'm having a particular issue with being able to change the quantity of a particular item in question. It remains unchanged regardless of how many times I call the function from main.cpp.
class ClassA {
public:
void SetQuantity(int quantityToGet);
...
private:
int itemQuantity;
...
};
void ClassA::SetQuantity(int quantityToGet) {
itemQuantity = quantityToGet;
}
class ClassB {
public:
ClassB();
void UpdateItemQnty();
int FindItemLoc(string targetItem);
...
private:
vector<ClassB> itemsInVector;
...
};
void ClassB::UpdateItemQnty() {
ClassA currItem;
string targetName;
int newQuantity;
int itemLoc = -1;
cout << "Enter the item name: ";
getline(cin, targetName);
itemLoc = FindItemLoc(targetName);
cout << "Enter the new quantity: ";
cin >> newQuantity;
cin.ignore();
if (itemLoc > -1) {
currItem = itemsInVector.at(itemLoc);
currItem.SetQuantity(newQuantity); // FIXME (???)
}
else {
cout << "Item not found in vector. Nothing modified." << endl;
}
}
I'm not getting any errors and the functions I didn't mention or show definition/declarations for, all work properly. I think I need to use a pointer, but I'm not sure how.
I'll likely delete this after I receive an answer based on the response
Thank You
I can see two strange things in your code:
Your are assigning a ClassA object with ClassB object (above the call of setQuantity). That should give an error, but I guess you made a typo in your post...
when retrieving the data from the list, your are copying it to the local variable currItem! Hence you are only changing a local copy, not the data in the list.
Declaring currItem as a reference would do the trick in this case, but the declaration of a reference object requires an assignment:
ClassA &currItem = itemsInVector.at(itemLoc);
currItem.SetQuantity(newQuantity);
You're simply setting the temporary variable's value. Try this,
itemsInVector[itemLoc].SetQuantity(newQuantity);
I've began making a program in linux with c++ and I'm trying to make it work on windows. It compiles fine, but when run I get this error: "1 [main] Trails of Cold Steel Simulator 8748 cygwin_exception::open_stackdumpfile: Dumping stack trace to Trails of Cold Steel Simulator.exe.stackdump". In the stack trace this exception occurs: "Exception: STATUS_ACCESS_VIOLATION". Here's some code;
#include "Tachi.h"
#include "AutumnLeafCutter.h"
#include <iostream>
#include "Weapon.h"
#include "Armour.h"
#include "Shoes.h"
int main() {
int stats[12] = {15,110,10,4,2,1,2,4,4,3,7,1};
Tachi* Tachi1 = new Tachi(stats, "Tachi");
Tachi1->addEquipment(new PracticeSword());
Tachi1->addEquipment(new LeatherJacket());
Tachi1->addEquipment(new WorkBoots());
Tachi1->addMasterQuartz(new Forcelvl1());
std::string input;
std::cout << "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
while(input != "q") {
std::cout << "Your current stats are:" << std::endl;
std::cout << "\n";
std::cout << "HP EP STR DEF ATS ADF SPD DEX AGL MOV RNG" << std::endl;
for(int i = 0; i < 12; i += 1) {
std::cout << Tachi1->getBaseStats()[i] << " ";
}
std::cout << "\n\n";
std::cout << "Select a Craft by typing its name:" << std::endl;
std::cout << std::endl;
for(int i = 0; i < Tachi1->getCrafts().size(); i++) {
std::cout << Tachi1->getCrafts()[i]->getName() << std::endl;
}
std::cout << std::endl;
getline(std::cin, input);
if(Tachi1->findCraft(input) != NULL) {
Tachi1->useCraft(input);
} else {
std::cout << "You do not have this craft." << std::endl;
}
std::cout << "\n\n\n";
}
}
Im extremely sorry for any formatting, I've never posted here. The error comes from lines 14,15,16 and 18. When I replaced all the "new xxx()" with NULL and comment out the body of the function with them, the program works. It does this for both addEquipment() and addMasterQuartz(). This is the functions;
void Character::addEquipment(Equipment* e) {
equipment.push_back(e);
std::cin.get();
for(int i = 0; i < 12; i++) {
baseStats[i] += equipment[equipment.size()]->getStatsModifier()[i];
}
}
and
void Character::addMasterQuartz(MasterQuartz* mq) {
masterQuartz = mq;
for(int i = 0; i < 12; i++) {
baseStats[i] += masterQuartz->getStatsModifier()[i];
}
}
Im guessing its a problem with the baseStats[i] += xxx stuff as its the only thing that occurs in both, but I have no idea how to fix that. It could also occur when the stuff is made using new xxx().
I can provide whatever else is needed. Thanks!!!!
EDIT:
I kept testing and the problem seems to lie in the creating of the objects. It worked on linux. Here is one of the object codes, they are all similiar and all crash the program;
#include "Armour.h"
Armour::Armour(int* sm, std::string n):Equipment(sm, n) {}
LeatherJacket::LeatherJacket():Armour(stats, armourName) {}
with header file;
#ifndef ARMOUR_H
#define ARMOUR_H
#include "Equipment.h"
class Armour:public Equipment {
public:
Armour(int* sm, std::string n);
};
class LeatherJacket:public Armour {
int stats[12] = {0,0,0,5,0,0,0,0,0,0,0,0};
std::string armourName = "Leather Jacket";
public:
LeatherJacket();
};
#endif
As soon as I remembered I did this I tried compiling (I think) with -std=c++11, it didnt help.
This is your error
baseStats[i] += equipment[equipment.size()]->getStatsModifier()[i];
By definition this is an out of bounds access on your vector, if a vector has a certain size, then the valid indexes are 0 to size - 1, not 0 to size.
It's fairly obvious that you wanted to access the last item in the vector. You can do that like this
baseStats[i] += equipment[equipment.size() - 1]->getStatsModifier()[i];
but even clearer is to use the back method.
baseStats[i] += equipment.back()->getStatsModifier()[i];
Another way would be to use the e variable you've just pushed onto the vector.
baseStats[i] += e->getStatsModifier()[i];
Adding some detail to the problem spotted by Useless, this code is incorrect.
class LeatherJacket : public Armour {
int stats[12] = {0,0,0,5,0,0,0,0,0,0,0,0};
std::string armourName = "Leather Jacket";
public:
LeatherJacket();
};
LeatherJacket::LeatherJacket() : Armour(stats, armourName) {}
The problem is the order in which things happen. First the Armour constructor is called, then the stats and armourName variables are initialised. So the call to the Armour constructor is using uninitiialised variables and will likely crash.
Several solutions possible, the best is probably to use virtual functions.
Making a couple of assumptions about Equipment (which isn't specified the question) it seems you should do something like this.
// header file
class Equipment
{
public:
virtual ~Equipment() {}
virtual std::string getName() const = 0;
virtual const int* getStatsModifier() const = 0;
};
class Armour : public Equipment
{
};
class LeatherJacket : public Armour
{
static const int stats[12];
public:
virtual std::string getName() const { return "Leather Jacket"; }
virtual const int* getStatsModifier() const { return stats; }
};
// source file
const int LeatherJacket::stats[12] = {0,0,0,5,0,0,0,0,0,0,0,0};
This answer adds pure virtual functions to the base class Equipment (which has become an interface), and implements those functions in LeatherJacket. Because the functions are virtual the appropriate function will always be called and there no need to pass the information down to Equipment. Also since it seems to be per-class constant data, stats has been made static const. Until you get to C++17 static const arrays must be defined in a source file, not the header file, as shown above.
Firstly, I'm going to replace the int[12] arrays with a proper type. Partly so the magic number 12 isn't littered all over the code and hard to change later, and partly because it will behave better (ie, not decay to a pointer in some contexts). This needs C++11.
#include <array>
using Stats = std::array<int, 12>;
To me it looks like Armour should have stats and a name, initialized from the arguments passed to its constructor (which you currently ignore).
Like so:
class Armour: public Equipment {
public:
Stats m_stats;
std::string m_name;
Armour(Stats const& s, std::string const &n) : m_stats(s), m_name(n) {}
};
You were already passing those two arguments to the constructor - you just weren't doing anything with them. Now you are.
This means that when we later have leather, scale, chain and plate subclasses, I can have a pointer of type Armour* and not need to worry about which subclass I'm looking at: the stats are available right there in the base class.
I made the members public, which is generally bad style, to save space. It might not matter for your use. I named the members with the m_ prefix so they can't accidentally get confused with similarly-named non-members. It's broadly good style but not essential.
LeatherArmour doesn't need an additional copy per instance, it just needs one of each for the whole class - so they should be const static members.
class LeatherJacket: public Armour {
static const Stats stats {0,0,0,5,0,0,0,0,0,0,0,0};
static const std::string name{"Leather Jacket"};
public:
LeatherJacket() : Armour(stats, name) {}
};
I made the LeatherJacket-specific stat values static const by writing static const in front of them.
The static means that every LeatherJacket has the same base stats, so you don't need a copy per instance, just one copy for the whole class. It's const because the base stats for leather jackets never change over time. You still have the base class member Armour::m_stats which can change as your individual leather jacket gets damaged, repaired, buffed or whatever.
Again, the LeatherJacket constructor was already passing (the equivalent of) these members to the base class constructor, but now they already exist (see the link above about static storage duration). The original instance variables didn't exist when you used them, because the derived (LeatherJacket) object and its data members aren't really constructed until after the base class subobject.
I have a task to create an object Stos which would feature a heap of objects Obiekt, to which I could add things as I please.
In order to make the program better support dynamic arrays I decided to use a Vector.
The whole implementation seems to run perfectly, the returned value is completely off.
Here is an example with code:
class Obiekt {
private:
int id;
public:
Obiekt::Obiekt(int i) {
id = i;
}
void Obiekt::display() {
cout << "This object has id of: " << id << endl;
}
};
class Stos {
private:
vector < Obiekt* > stos;
public:
Stos::Stos(Obiekt n) {
add(n);
}
void Stos::add(Obiekt n) {
stos.push_back(&n);
}
void Stos::display() {
cout << endl << "===HEAP DISPLAY===" << endl;
for (int i = 0; i < stos.size(); i++) {
stos[i]->display();
}
}
};
void Zad1()
{
Obiekt obj1(5);
Obiekt obj2(23);
Stos s1(obj1);
s1.add(obj2);
s1.display();
getchar();
}
And the outcome being:
===HEAP DISPLAY===
This object has id of: -858993460
This object has id of:9805925
I'm not a cpp expert, and believe the issue is related to the stos.push_back(&n) portion, but I can't catch the moment the id gets so distorted.
It's probably a noob question, so sorry for that on start.
Any help would be amazing.
The issue with your code as O'Neil correctly explained is that you're adding the pointer to a copy of the Obiekt object. So basically, you create your object in main, and pass it to the constructor and the .add function in Stos. You then add the pointer to the vector. When the function finishes, the copy that was passed is destroyed and the pointer in your vector is dangling.
There are two ways to fix this:
1 Pass by reference
This is very simple, basically you just add an ampersand to your function parameters. For instance:
void Stos::add(Obiekt &n) {
stos.push_back(&n);
}
This will ensure that the object isn't destroyed at the end of the function
2 Don't use pointers
Another way of getting your problem to work is to avoid using pointers at all. Your vector will actually copy the contents of the Obiekt object into it. For example:
vector < Obiekt > stos; // notice how we define it without the pointer type
...
void Stos::add(Obiekt n) {
stos.push_back(n); // Creates copy which will then contain the correct value
}
The parameters Obiekt n in
Stos::Stos(Obiekt n) {
add(n);
}
void Stos::add(Obiekt n) {
stos.push_back(&n);
}
are temporary copies destroyed immediatly after each call.
You have to use a reference Obiekt & n instead, or better: by pointer Obiekt * n.
I'm reluctant to assert that the objects exist at the time display is called.
Problem(s)
According to GCC's implementation they don't.
They fall out of scope and are immediately destructed. Give "Obiekt" a non-trivial destructor and this behavior becomes obvious:
~Obiekt(){std::cout << "Bye from: " << it << std::endl;}
Secondarily, note that you shouldn't specify the class membership for functions defined within the class itself (no class_name::function_name(parameters), just function_name(parameters) )
Possible Fix
You (might) want to changing "Stos" such that:
Stos(Obiekt &n) {add(n);}
void add(Obiekt &n) {stos.push_back(&n);}
I am new to classes and constructors. This program requires user to input name for two circles. I defined a default constructor to set parameters for radius and name and another constructor to accept them as arguments. I believe there is an issue with setName and also it tells me the constructors have already been defined. Any help is appreciated!
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
class Circle
{
private:
double pi = 3.14;
double radius;
string *name;
public:
Circle();
Circle(double, string);
Circle::Circle()
{
radius = 0.0;
*name = nullptr;
}
Circle::Circle(double r, string n)
{
radius = r;
*name = n;
}
~Circle()
{
delete[] name;
}
void setRadius(double r)
{
if (r >= 0)
radius = r;
else
{
cout << "Invalid radius\n";
exit(EXIT_FAILURE);
}
}
double getRadius()
{
return radius;
}
double getArea()
{
return pi* radius * radius;
}
double getCircumference()
{
return 2 * pi * radius;
}
void setName(string n)
{
*name = n;
}
string getName()
{
return *name;
}
};
int main()
{
Circle circle1;
Circle circle2;
double circRad1;
double circRad2;
string name1;
string name2;
cout << "Enter the name for circle 1: ";
getline(cin, name1);
cout << "Enter the name for circle 2: ";
getline(cin, name2);
cout << "Enter the radius for cirle 1: ";
cin >> circRad1;
cout << "Enter the radius for cirle 2: ";
cin >> circRad2;
circle1.setRadius(circRad1);
circle2.setRadius(circRad2);
circle1.setName(name1);
circle2.setName(name2);
cout << "Circle 1 name: " << circle1.getName() << "\n";
cout << "Circle 1 radius: " << circle1.getRadius() << "\n";
cout << "Circle 1 area: " << circle1.getArea() << "\n";
cout << "Circle 1 circumfrence: " << circle1.getCircumference() << "\n";
cout << "\n";
cout << "Circle 2 name: " << circle2.getName() << "\n";
cout << "Circle 2 radius: " << circle2.getRadius() << "\n";
cout << "Circle 2 area: " << circle2.getArea() << "\n";
cout << "Circle 2 circumfrence: " << circle2.getCircumference() << "\n";
return 0;
}
Problems I see:
Constructors
You have:
Circle();
Circle(double, string);
Circle::Circle()
{
radius = 0.0;
*name = nullptr;
}
Circle::Circle(double r, string n)
{
radius = r;
*name = n;
}
That is not correct since the first two lines declare the constructors while you declare, and define, them again with incorrect syntax.
Remove the first two lines.
Use of name
It's not clear why you are using string* for name. Make it an object, not a pointer.
string name;
Then, change the constructors to:
// Use the default constructor to initialize name
Circle() : radius(0.0) {}
Circle(double r, string n) : radius(r), name(n) {}
You may remove the destructor altogether. If you insist on having one, change it to (there is no need for delete name any more):
~Circle() {}
Change setName() to:
void setName(string n)
{
name = n;
}
Change getName() to:
string getName() const
{
return name;
}
PS Your attempted code indicates to me that you will benefit from going through the fundamentals of the language from a good book. See The Definitive C++ Book Guide and List for ideas.
I just want to add to the previous answer since I don't have enough points to comment.
In his constructor, he's using what's called an initializer list:
i.e.
Circle() {
foo = 0;
bar = 0;
}
versus (initializer list):
Circle() : foo(0), bar(0) {}
When you're just initializing variables, the preferred practice is almost always the list format. This is because it provides the arguments to construct your object before it is instantiated. This would allow you to construct objects whose "identity" isn't known until runtime (i.e. a variable-type object, or an object which could take on more than one type, although these aren't quite native to c++), or a const value.
I am a little suspicious about your instructor having you create dynamic objects in your first programming course, but since this seems to be the case,
The reason you can't simply pass in a string object and assign the pointer to it is because a pointer is strictly an address to an already existing object. This would only work if the string were passed in by reference, and then the syntax might be:
void foo(std::string& str) {
name = &str;
}
When you pass in by value (without the ampersand) a copy of your object is being made to pass in via the parameter. This copy doesn't exactly have it's own home in memory yet, and it's definitely not the same home as what you passed in the parameter. So when you try to give it's address to the pointer, the compiler wants to complain because the address you're trying to save is going to disappear as soon as this scope is over with (the next } is hit).
You can, however, create a permanent object with a copied value. This is when you allocate dynamic memory on heap (normally it's on the stack). This would look something like:
void foo(std::string str) {
name = new std::string(str);
}
This will allow your name pointer to point to a newly created object on the heap. This is why you need the delete[] expression in your deconstructor, because the compiler can't manage dynamic memory for you, so you have to make sure to free it before the program ends.
Note that the [] are needed because a string is actually an array of characters. When you dynamically allocate an array, the [] notation will ensure that the memory until a sentinel value is read is freed. A sentinel character almost always refers to NULL or 0 on the ASCII chart.
If it were an int being freed, the syntax would just be:
delete x;
One last note. In your private section you have a variable called pi which is default initialized to 3.14. This is presumably because this is a value which will often be referred to and is common amongst all circles. When you have such common variables which will be the same in every instance of that class, you'll want to use what are called static variables. This is a variable which is allocated once, and which everyone associated with that variable has access to. Also, because you don't want pi to change, it should be const. It might look like this:
private:
static const double PI = 3.14;
What this will do is create one object called PI, and that exact same PI object will be used in every single circle you create. This will vastly cut down on the memory usage of that object, assuming you may create many. It is also good to note that typically const variables are capitalized, and non-const variables are not.
I agree with all of the points made by #RSahu, but will attempt to answer your specific issues.
Disclaimer: Using pointers as you do in this assignment is unnecessary and dangerous. It is even more unusual to require the use of them in this situation as pointers are a notoriously difficult concept for beginners to grasp.
Defining Constructors
You are defining each constructor twice.
Circle();
Circle::Circle()
{
// ...
}
and then
Circle(double, string);
Circle::Circle(double r, string n)
{
// ...
}
You only need to define them once. If you are declaring and defining them at the same time then the following is sufficient:
Circle()
{
// ...
}
If you want to declare and define them separately then you can do:
class Circle
{
public:
// Declare the constructor
Circle();
};
// Then later in some source, define it
Circle::Circle()
{
// ...
}
Implementing Constructors
You have crucial mistakes in both constructors (ignoring the fact that you are forced to use string*).
First,
Circle()
{
radius = 0.0;
*name = nullptr;
}
When you perform *name = nullptr you are dereferencing the name pointer and assigning it to nullptr.
This is bad for multiple reasons:
name has not been set. You are dereferencing a garbage pointer and setting it to nullptr. This is a crash.
Even if name had been initialized, you are setting the string object that it points to to nullptr which is another crash.
The proper way to initialize this would be as:
Circle()
: radius{ 0.0 },
name{ nullptr }
{
}
Let us look at the other constructor now.
Circle(double r, string n)
{
radius = r;
*name = n;
}
Again, radius is set correctly (mostly) but we have major issues with name.
name once again is uninitialized. So we are setting a non-existant string that name points to to n.
Now, here we are actually spared a bit of good luck. If you instead were performing
name = &n;
Then that would be bad as n is a temporary object. Once we leave the constructor our name would be pointing to garbage and you would crash the next time you try to access it.
But so how do we fix this constructor? I would do it like so:
Circle(double const r, string n)
: radius{ r },
name{ new string{n} }
{
}
In name{ new string{n} } we are setting name to a new string object that is initialized by the value in n.
Hope you are beginning to understand why in my disclaimer I do not approve of the requirement of using string* ...
Fixing setName
So, your implementation of setName is almost OK.
If we created an object of Circle using the second constructor it would be fine. Our string that name points to would simply be set the value of n.
But what if we are using a Circle created via the first constructor? Then we would be dereferencing a nullptr and attempting to set it the value of n. Crash.
I would actually fix this problem in your first constructor by changing it to:
Circle()
: radius{ 0.0 },
name{ new string }
{
}
So now we know name always points to a valid string object.
Finally, the Destructor
In the destructor you are using the incorrect delete[].
Use delete[] when deleting a dynamic array of objects. string is a single object, and thus should use delete.
I personally also think it is a good habit to set any deleted pointer to nullptr so that any common nullptr checks will work and not fail due to garbage.
~Circle()
{
delete name;
name = nullptr;
}