I do not know how to make constructors for an object with multiple raw pointers. I understand how to make it for one but do not understand multiple.
I have tried to write a new constructor during initialization, which then specifies what is accessed from the donor object.
I would like to know a way to make multiple constructors that extract different l values from one object depending on the order of initialization, however, I cannot find information about how to do this. I have some examples below.
class Movie {
publuc:
std::string *movie_name;
std::string *age_rating;
int *view_count;
int *rating;
Movie::Movie(const Movie &source , const Movie &source , const Movie &source , const Movie &source)
: {movie_name = new string;
*movie_name source;},
{age_rating = new string;
*age_rating source;},
{view_count = new int;
*view_count source;},
{source.rating = new int;
*source.rating source;}
I am at loss for understanding, I know I am missing something because this problem must be solvable. Please help me c++ master wherever you are.
You shouldn't be using raw pointers at all here. There is no reason for it. It's just an invitation to make tons of errors.
Secondly, the syntax you have for initializers is very broken, that's not how the syntax works at all. I'll put in an example of what initializers should look like in here so you can see. You also misunderstand what new does. You don't need any of the assignments that you're doing. Not one of them.
Also, you've misspelled public as publuc. And the way you declare your constructor is not correct for declaring it as a class member.
You know, almost all the problems you have, the compiler should've given you at least vaguely sensible error messages about, and you shouldn't need us to fix them for you.
Here is an example class that uses some pointer members. Note that if you are using pointer members, especially as a C++ beginner, you are almost certainly doing something wrong. Bare pointers should be among the last things you learn about in C++, not the first:
class IHaveSomePointers {
public:
IHaveSomePointers(bool const &mybool, int const &myint)
: mybool_{new bool(mybool)}, myint_{new int(myint)}
{ }
~IHaveSomePointers() {
delete mybool_;
delete myint_;
}
IHaveSomePointers(IHaveSomePointers const &) = delete;
void operator =(IHaveSomePointers const &) = delete;
private:
bool *mybool_;
int *myint_;
};
This class does have one problem. If allocation of myint_ throws an exception, there will be a memory leak. This kind of thing is why you do not use raw pointers in C++, especially for member variables, and most especially if the thing they're going to be pointing at is allocated with new.
I deleted the copy constructor and assignment operator because they need special implementations for classes that contain raw pointers. I notice that you appear to have been trying to define your own copy constructor, but it's hard to tell because your constructor declaration is so very garbled.
Here is how this class should be written:
class IDontHavePointersBecauseThatsBad {
public:
IDontHavePointersBecauseThatsBad(bool const &mybool, int const &myint)
: mybool_{mybool}, myint_{myint}
{ }
private:
bool mybool_;
int myint_;
};
If you absolutely must dynamically allocate things, do this:
#include <memory>
class ForSomeBizarreReasonIDyanmicallyAllocate {
public:
ForSomeBizarreReasonIDynamicallyAllocate(bool const &mybool, int const &myint)
: mybool_{::std::make_unique<bool>(mybool)},
myint_{::std::make_unique<int>(myint)}
{ }
private:
::std::unique_ptr<bool> mybool_;
::std::unique_ptr<int> myint_;
};
That class doesn't need a destructor to make sure it deletes the memory it allocates. It doesn't have a potential memory leak. It's all around a better class.
Your copy constructor is all wrong. It needs to look like this instead:
class Movie {
public:
std::string *movie_name;
std::string *age_rating;
int *view_count;
int *rating;
Movie(const Movie &source) :
movie_name(new string(*(source.movie_name))),
age_rating(new string(*(source.age_rating))),
view_count(new int(*(source.view_count))),
rating(new int(*(source.rating)))
{
}
...
};
You would also need to implement a destructor and a copy assignment operator, and in C++11 and later a move constructor and move assignment operator, per the Rule of 3/5/0, eg:
class Movie {
public:
std::string *movie_name;
std::string *age_rating;
int *view_count;
int *rating;
Movie() :
movie_name(new string),
age_rating(new string),
view_count(new int(0)),
rating(new int(0))
{
}
Movie(const Movie &source) :
movie_name(new string(*(source.movie_name))),
age_rating(new string(*(source.age_rating))),
view_count(new int(*(source.view_count))),
rating(new int(*(source.rating)))
{
}
Movie(Movie &&source) :
movie_name(source.movie_name),
age_rating(source.age_rating),
view_count(source.view_count),
rating(source.rating)
{
source.movie_name = nullptr;
source.age_rating = nullptr;
source.view_count = nullptr;
source.rating = nullptr;
}
~Movie()
{
delete movie_name;
delete age_rating;
delete view_count;
delete rating;
}
Movie& operator=(const Movie &source)
{
if (&source != this)
{
*movie_name = *(source.movie_name);
*age_rating = *(source.age_rating);
*view_count = *(source.view_count);
*rating = *(source.rating);
}
return *this;
}
Movie& operator=(Movie &&source)
{
Movie temp(std::move(source));
std::swap(movie_name, temp.movie_name);
std::swap(age_rating, temp.age_rating);
std::swap(view_count, temp.view_count);
std::swap(rating, temp.rating);
return *this;
}
};
You can mitigate some of the risk of manually managing memory allocations by using smart pointers:
#include <memory>
class Movie {
public:
std::unique_ptr<std::string> movie_name(new string);
std::unique_ptr<std::string> age_rating(new string);
std::unique_ptr<int> view_count(new int(0));
std::unique_ptr<int> rating(new int(0));
Movie() = default;
Movie(const Movie &source) :
movie_name(new string(*(source.movie_name))),
age_rating(new string(*(source.age_rating))),
view_count(new int(*(source.view_count))),
rating(new int(*(source.rating)))
{
}
Movie(Movie &&source) = default;
~Movie() = default;
Movie& operator=(const Movie &source)
{
if (&source != this)
{
*movie_name = *(source.movie_name);
*age_rating = *(source.age_rating);
*view_count = *(source.view_count);
*rating = *(source.rating);
}
return *this;
}
Movie& operator=(Movie &&source) = default;
};
But really, there is no good reason to be using pointers at all in this situation. Get rid of the pointers altogether and let the compiler auto-generate appropriate constructors, destructor, and assignment operators that do all of the hard work of managing memory and copying values for you:
class Movie {
public:
std::string movie_name;
std::string age_rating;
int view_count = 0;
int rating = 0;
// everything is auto-generated for you!
};
Thx you for the answers, this isn't homework though its late-night scribblings. I was really tired and trying to go ahead of what I have studied. Although on the bright side I haven't studied smart pointers yet so I feel less stupid, I just wanted to say thanks for being more constructive that I usually am over the internets.
Related
In C++11/14, an object can be transfered by move or smark pointer.
(1) This is an example for move:
class MoveClass {
private:
int *tab_;
int alloc_;
void Reset() {
tab_ = nullptr;
alloc_ = 0;
}
void Release() {
if (tab_) delete[] tab_;
tab_ = nullptr;
alloc_ = 0;
}
public:
MoveClass() : tab_(nullptr), alloc_(0) {}
~MoveClass() {
Release();
}
MoveClass(MoveClass && other) : tab_( other.tab_ ), alloc_( other.alloc_ ) {
other.Reset();
}
MoveClass & operator=(MoveClass && other) {
if (this == &other) return *this;
std::swap(tab_, other.tab_);
std::swap(alloc_, other.alloc_);
return *this;
}
void DoSomething() { /*...*/ }
};
When we use this movable MoveClass, we can write code like this :
int main() {
MoveClass a;
a.DoSomething(); // now a has some memory resource
MoveClass b = std::move(a); // move a to b
return 0;
}
Always write move-constructor/move-operator= is boring, use shared_ptr/unique_ptr some times have the same effect, just like java, reference/pointer everywhere.
(2) Here is the example:
class NoMoveClass {
private:
int *tab_;
int alloc_;
void Release() {
if (tab_) delete[] tab_;
tab_ = nullptr;
alloc_ = 0;
}
public:
NoMoveClass() : tab_(nullptr), alloc_(0) {}
~NoMoveClass() {
Release();
}
MoveClass(MoveClass && other) = delete;
MoveClass & operator=(MoveClass && other) = delete;
void DoSomething() { /*...*/ }
};
We can use it like this:
int main() {
std::shared_ptr<NoMoveClass> a(new NoMoveClass());
a->DoSomething();
std::shared_ptr<NoMoveClass> b = a; // also move a to b by copy pointer.
return 0;
}
Is it a good habit to always use the 2nd one?
Why many libraries, STL use the 1st one, not the 1st one ?
Always write move-constructor/move-operator= is boring
You almost never need to write your own move constructor/assignment, because (as you mentioned) C++ supplies you with a number of basic resource managers - smart pointers, containers, smart locks etc.
By relying on those in your class you enable default move operations and that results in minimal code size as well as proper semantics:
class MoveClass {
private:
std::vector<int> data;
public:
void DoSomething() { /*...*/ }
};
Now you can use your class as in (1) or as a member in other classes, you can be sure that it has move semantics and you did it in the minimal possible amount of code.
The point is one usually only needs to implement move operations for the most low-level classes which are probably covered already by STL, or if some weird specific behavior is needed - both cases should be really rare and not result in "Always writing move-constructor/move-operator=".
Also notice that while approach (1) is unnecessarily verbose, (2) is just unacceptable - you have a resource managing class that doesn't do its job and as a result you have to wrap it in smart pointers everywhere in your code, making it harder to understand and eventually resulting in even more code than (1)
Code like this from one of my books for example:
class HasPtr {
public:
HasPtr(const HasPtr& h): ps(new std::string(*h.ps)), i(h.i) { }
HasPtr(const std::string &s = std::string()): ps(new std::string(s)), i(0) { }
HasPtr& operator=(const HasPtr&);
~HasPtr() { delete ps; }
private:
std::string *ps;
int i;
};
HasPtr& HasPtr::operator=(const HasPtr &rhs){
auto newp = new string(*rhs.ps); // copy the underlying string
delete ps; // free the old memory
ps = newp; // copy data from rhs into this object
i = rhs.i;
return *this; // return this object
}
Seems like the inside of the operator= could just be:
*ps = *rhs.ps
i = rhs.i
return *this;
With no need to delete the pointer first, seems redundant to do so. It did mention it is written in a way to leave the object in a suitable state should an exception occur but didn't divulge past that, but I don't see what exception could occur that even my alternative wouldn't handle. Why is there a need to delete the object first before assigning?
In this case, yes, that would be fine.
You're not leaking the dynamically-allocated string: you're re-using it.
This looks fine to me.
And you're right, std::string assignment already offers a strong exception guarantee so you will still leave the object in its original state should an exception occur copying the string.
Of course there is no reason to allocate a std::string with new like that. You could just write this instead:
class HasNoPtr {
public:
HasNoPtr(const std::string& s): ps(s), i(0) { }
private:
std::string ps;
int i;
};
After making a lot of changes to a project, I created an error that took me quite a while to track down.
I have a class which contains a dynamically allocated array. I then create a dynamic array of this class. I can then delete[] that array. But, if I replace an item in the array before deleting it, it causes an error. In debug mode, it gives an assertion message from dbgdel.cpp "Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)". Here is a small program to demonstrate.
class SomeClass {
public:
int *data;
SomeClass() {
data = nullptr;
}
SomeClass(int num) {
data = new int[num];
}
~SomeClass() {
if (data != nullptr) {
delete[] data;
}
}
};
int main(int argc, char *args[]) {
SomeClass *someArray = new SomeClass[10];
//If you comment this out, there is no error. Error gets thrown when deleting
someArray[0] = SomeClass(10);
delete[] someArray;
return 0;
}
I'm curious, why does this happen? When the item in the array gets replaced, its destructor gets called. Then the new item allocates its data in a location separate from the array. Then delete[] calls the destructors of all the objects in the array. When the destructors get called, they should delete the item's data array. I can't imagine what the problem is, but I'd like if someone could explain.
Your class is broken: It has a non-trivial destructor, but you do not define copy constructors and copy assignment operators. That means that the class cannot be correctly copied or assigned-to (since the destructible state is not copied or assigned appropriately), as you are noticing in your example code.
You can either make your class uncopiable (in which case your code won't compile any more), or move-only, in which case you need to define move construction and move-assignment, or properly copyable by implementing a deep copy of the data.
Here's how, add the following definitions:
Non-copyable:
SomeClass(SomeClass const &) = delete;
SomeClass & operator(SomeClass const &) = delete;
Moveable-only:
SomeClass(SomeClass const &) = delete;
SomeClass(SomeClass && rhs) : data(rhs.data) { rhs.data = nullptr; }
SomeClass & operator(SomeClass const &) = delete;
SomeClass & operator(SomeClass && rhs) {
if (this != &rhs) { delete data; data = rhs.data; rhs.data = nullptr; }
return *this;
}
Copyable:
SomeClass(SomeClass const & rhs) : ptr(new int[rhs->GetSizeMagically()]) {
/* copy from rhs.data to data */
}
SomeClass & operator(SomeClass const & rhs) {
if (this == &rhs) return *this;
int * tmp = new int[rhs->GetSizeMagically()];
/* copy data */
delete data;
data = tmp;
}
// move operations as above
The upshot is that the nature of the destructor determines the invariants of the class, because every object must be consistently destructible. From that you can infer the required semantics of the copy and move operations. (This is often called the Rule of Three or Rule of Five.)
Kerrek SB s answer is great. I just want to clarify that in your code memory is freed twice.
This code
someArray[0] = SomeClass(10);
is the same as this
SomeClass temp(10);
someArray[0] = temp; //here temp.data is copied to someArray[0].data
Then ~SomeClass() is called for temp and data is freed first time.
Here
delete[] someArray;
~SomeClass() is called for someArray[0] and data is freed second time.
I can't figure out why I get error for the code below.
The instances of object A will be pushed into a vector (vectorA.push_back(A a)) continuously. So sometimes, vectorA needs to be reallocated; the destructor will be called, which is where the destructor of A gets called, then the error message appears.
class A
{
long filePos;
union {
Recording* recording;
UINT64 timeStamp;
};
public:
inline A(long fpos, UINT64 ts) : filePos(fpos), timeStamp(ts) {}
~A()
{
if (getDetailedType() == RECORDING_TYPE)
if (recording)
delete recording; // error: scalar deleting destructor ???
}
inline short getDetailedType() const { return (short)(timeStamp % 5); }
A(const A& edi)
{
filePos = edi.filePos;
if (getDetailedType() == RECORDING_INFO)
recording = edi.recording;
else
timeStamp = edi.timeStamp;
}
}
class Recording : protected RECORDINGS
{
UINT64 timestamp;
float scalar;
public:
~Recording() // with or without this dtor, got same error
{
}
inline Recording()
{
timestamp = 0;
scalar = 2.0;
time = 0;
rate = 30;
type = 1;
side = 1;
}
}
typedef struct
{
UINT32 time;
float rate;
int type;
int side;
} RECORDINGS;
Your copy constructor does a shallow copy. So, now you have two objects that both have the same recording pointer.
You should either do a deep copy, or ensure the ownership is properly transferred (by using something like std::unique_ptr<Recording> if C++11 is available.
See This question on the difference between deep and shallow copies.
Let's look at some examples:
class ABadCopyingClass
{
public:
ABadCopyingClass()
{
a_ = new int(5);
}
~ABadCopyingClass()
{
delete a_;
}
private:
int* a_;
};
The above class is bad because the default copy constructor and assignment operator will perform a shallow copy, and lead to two objects both thinking that they own the underlying a_ object. When one of them goes out of scope, the a_ will be deleted, and the other one will be left with a dangling pointer that will eventually lead to a crash.
class ABetterCopyingClass
{
public:
ABetterCopyingClass()
a_(new int(5))
{
}
ABetterCopyingClass(const ABetterCopyingClass& r)
{
a_ = new int(*r.a_);
}
ABetterCopyingClass& operator=(const ABetterCopyingClass& r)
{
// in the case of reassignment...
delete a_;
a_ = new int(*r.a_);
return *this;
}
~ABetterCopyingClass()
{
delete a_;
}
private:
int* a_;
};
This class improved our situation a little (note, that the normal error checking is left out in this simple example). Now the copy constructor and assignment operator properly perform the necessary deep copying. The drawback here is the amount of boilerplate code we had to add -- it's easy to get that wrong.
class ACannonicalCopyingClass
{
public:
ACannonicalCopyingClass()
: a_(new int(5))
{
}
ACannonicalCopyingClass(ACannonicalCopyingClass&& moved_from)
{
a_ = std::move(moved_from.a_);
}
private:
std::unique_ptr<int> a_;
};
This example (C++11 only) is even better. We've removed a significant amount of boilerplate code, however the semantics here are a bit different. Instead of deep copying, we get in this case transfer of ownership of the underlying a_ object.
The easiest version (C++11 only) to implement is the version that provides shared ownership of the underlying a_ object. This is the version that is most similar to your provided example, with the added bonus that it does not cause a crash.
class ASharedCopyingClass
{
public:
ASharedCopyingClass()
: a_(std::make_shared<int>(5))
{
}
private:
std::shared_ptr<int> a_;
};
This version can be copied at will, and the underlying a_ object will happily be reference counted. The last copy to go out of scope will set the reference count to 0, which will trigger the memory deallocation.
My psychic debugging skills tell me that you forgot to implement a copy constructor for A which then results in a double deletion of a Recording when the copy is destroyed.
The growth of the vector would trigger the copy-destroy pairs.
I have this class:
class CComputer {
public:
// constructor
CComputer(string name) {
this->name = name;
};
// overloaded operator << for printing
friend ostream& operator<<(ostream& os, const CComputer& c);
// adds some component for this computer
CComputer & AddComponent(Component const & component) {
this->listOfComponents.push_back(component);
return *this;
};
// sets address for this computer
CComputer & AddAddress(const string & address) {
this->address = address;
return *this;
};
string name;
string address;
list<Component> listOfComponents;
};
and then these classes:
// ancestor for other classes...It's really dummy yet, but I dunno what to add there
class Component {
public:
Component() {};
~Component() {};
};
class CCPU : public Component {
public:
CCPU(int cores, int freq) {
this->cores = cores;
this->freq = freq;
};
int cores;
int freq;
};
class CMemory : public Component {
public:
CMemory(int mem) {
this->mem = mem;
};
int mem;
};
Now I feed my CComputer class with some values:
CComputer c("test.com");
c . AddAddress("123.45.678.910") .
AddComponent(CCPU(8, 2400)) .
AddComponent(CCPU(8, 1200)).
AddComponent(CMemory(2000)).
AddComponent(CMemory(2000)));
And now I would like to print it out with all the info I've put in there (CCPU & CMemory details including)
but how to implement it, to be able to iterate through CComputer::listOfComponents and don't care if I acctually access CCPU or CMemory ? I can add it to that list, but I have really no idea, how to make it, to be able to access the variables of those components.
So the output should look like:
##### STARTING #####
CComputer:
name:test.com
address:123.45.678.910
CCPU:
cores:8,freq:2400
CCPU:
cores:8, freq:1200
CMemory:
mem:2000
CMemory:
mem:2000
###### FINISHED! #####
As others have mentioned, you need to implement a virtual function (e.g. virtual std::string ToString() const = 0;) in the base class that is inherited and overridden by each child class.
However, that isn’t enough. Your code exhibits slicing which happens when you copy your child class instances into the list: the list contains objects of type Component, not of the relevant child class.
What you need to do is store polymorphic instances. Values themselves are never polymorphic, you need to use (smart) pointers or references for this. References are out, however, since you cannot store them in a standard container (such as std::list). Using raw pointers is considered bad style nowadays, but judging from the naming conventions of your classes you don’t learn modern C++ in your class (sorry!).
Therefore, raw pointers is probably the way to go. Change your code accordingly:
Store a list of pointers:
list<Component*> listOfComponents;
Make the argument type of AddComponent a pointer instead of const&.
Call the function by passing a newed object, e.g.:
AddComponent(new CCPU(8, 2400))
Now your code leaks memory left, right and center. You need to implement a destructor to free the memory:
~CComputer() {
typedef std::list<Component*>::iterator iter_t;
for (iter_t i = listOfComponents.begin(); i != listOfComponents.end(); ++i)
delete *i;
}
But now your code violates the Rule of Three (read this article! It’s important, and it may be the most useful thing about C++ you’re going to learn in this programming class) and consequently you also need to implement the copy constructor and copy assignment operator. However, we can’t. Sorry. In order to implement copying for your class, you would have to implement another virtual function in your Component class, namely one that clones an object (virtual Component* Clone() const = 0;). Only then can we proceed.
Here’s a sample implementation in CCPU:
Component* Clone() const {
return new CCPU(cores, freq);
}
… this needs to be done in all classes deriving from Component, otherwise we cannot correctly copy an object of a type that derives from Component and is hidden behind a pointer.
And now we can implement copying in the CComputer class:
CComputer(CComputer const& other)
: name(name)
, address(addess) {
typedef std::list<Component*>::iterator iter_t;
for (iter_t i = other.listOfComponents.begin(); i != other.listOfComponents.end(); ++i)
listOfComponents.push_back((*i)->Clone());
}
CComputer& operator =(CComputer const& other) {
if (this == &other)
return *this;
name = other.name;
address = other.address;
listOfComponents.clear();
for (iter_t i = other.listOfComponents.begin(); i != other.listOfComponents.end(); ++i)
listOfComponents.push_back((*i)->Clone());
return *this;
}
This code is brittle, not thread-safe and error-prone and no competent C++ programmer would ever write this1. Real code would for instance use smart pointers instead – but as mentioned before I’m pretty sure that this would be beyond the scope of the class.
1 What does this make me now, I wonder?
Just add a virtual method to Class Component called e.g. toString(), which returns a string describing the component. Then you can iterate through all components and call toString() without worrying about exactly what each component is. If you do that, then for each computer you would be able to print out the values of all the components.
However, as pointed out in one of the comments, the example output you give in the question outputs the CCPU for all computers, then all the memory for all computers. To order the output like that, you'll need to add another virtual method to Component called e.g. getType() which returns an enum or integer that represents the type of the information. You can then have two for-next loops, one nested inside the other, where the outer loop iterates through all the types and the inner loop iterating through all the computers calling the toString() on all components which match the type specified in the outer for loop.
Here's something that implements this idea.
#include <iostream>
#include <string>
#include <list>
using namespace std;
int const TYPE_CCPU = 1;
int const TYPE_MEMORY = 2;
class Component {
public:
virtual int GetType() { return -1; }
virtual std::string ToString() const {
return "OOPS! Default `ToString` called";
}
};
class CComputer {
public:
typedef std::list<Component*>::iterator iter_t;
// constructor
CComputer(string name) {
this->name = name;
};
~CComputer() {
for (iter_t i = listOfComponents.begin(); i != listOfComponents.end(); ++i) {
delete *i;
}
}
// overloaded operator << for printing
friend ostream& operator<<(ostream& os, const CComputer& c);
// adds some component for this computer
CComputer & AddComponent(Component *component) {
this->listOfComponents.push_back(component);
return *this;
};
// sets address for this computer
CComputer & AddAddress(const string & address) {
this->address = address;
return *this;
};
void PrintType(int type) {
for (iter_t i = listOfComponents.begin(); i != listOfComponents.end(); ++i) {
if ((*i)->GetType() == type)
std::cout << (*i)->ToString() << '\n';
}
}
string name;
string address;
list<Component*> listOfComponents;
};
class CCPU : public Component {
public:
CCPU(int cores, int freq) {
this->cores = cores;
this->freq = freq;
};
int GetType() { return TYPE_CCPU; }
std::string ToString() const {
return "CCPU::ToString()";
}
int cores;
int freq;
};
class CMemory : public Component {
public:
CMemory(int mem) { this->mem = mem; };
int GetType() { return TYPE_MEMORY; }
std::string ToString() const {
return "CMemory::ToString()";
}
int mem;
};
typedef std::list<CComputer*>::iterator iter_c;
int main() {
list<CComputer*> computerlist;
CComputer *c1 = new CComputer("test.com"), *c2 = new CComputer("test2.com");
c1->AddAddress("123.45.678.910").
AddComponent(new CCPU(8, 1200)).
AddComponent(new CMemory(2000));
computerlist.push_back(c1);
c2->AddAddress("987.65.432.10").
AddComponent(new CCPU(8, 2400)).
AddComponent(new CMemory(4000));
computerlist.push_back(c2);
for(int t=TYPE_CCPU; t<=TYPE_MEMORY; t++)
for (iter_c i = computerlist.begin(); i != computerlist.end(); ++i) {
(*i)->PrintType(t);
}
for (iter_c i = computerlist.begin(); i != computerlist.end(); ++i) {
delete (*i);
}
}
Implement ToString() in each of your classes. In .NET this is a standard even the "object" type implements.