How can I create objects while adding them into a vector?
To answer the first part of your question, you must create an object of type Player before you can use it. When you say push_back(Player)
, it means "add the Player class to the vector", not "add an object of type Player to the vector" (which is what you meant).
You can create the object on the stack like this:
Player player;
vectorOfGamers.push_back(player); // <-- name of variable, not type
Or you can even create a temporary object inline and push that (it gets copied when it's put in the vector):
vectorOfGamers.push_back(Player()); // <-- parentheses create a "temporary"
To answer the second part, you can create a vector of the base type, which will allow you to push back objects of any subtype; however, this won't work as expected:
vector<Gamer> gamers;
gamers.push_back(Dealer()); // Doesn't work properly!
since when the dealer object is put into the vector, it gets copied as a Gamer object -- this means only the Gamer part is copied effectively "slicing" the object. You can use pointers, however, since then only the pointer would get copied, and the object is never sliced:
vector<Gamer*> gamers;
gamers.push_back(new Dealer()); // <-- Allocate on heap with `new`, since we
// want the object to persist while it's
// pointed to
How to add new objects to vector in C++
How about you first create the object and then push a copy into the vector?
Call to the overloaded constructor to store user input in object
Vehicle temp(VIN, Make, Model, Year, Price);
carList.push_back(temp);
But there's no need for the variable, really:
Call to the overloaded constructor to store user input in object
carList.push_back(Vehicle(VIN, Make, Model, Year, Price));
And if you have C++11, you can even construct the object directly in place:
Call to the overloaded constructor to store user input in object
carList.emplace_back(VIN, Make, Model, Year, Price);
Look ma, no copies!
How to create a vector of class objects in C++?
vector<Site> myStack();
This is actually a function declaration. The function is called myStack
and it returns a vector<Site>
. What you actually want is:
vector<Site> myStack;
The type of neighbours
at the moment will store copies of the objects, not references. If you really want to store references, I recommend using a std::reference_wrapper
(rather than using pointers):
vector<reference_wrapper<Site>> neighbors;
C++: Creating a vector of class objects
You are looking for what is called a vector
. A vector
is similar to an Array
but it is allowed to change its size during run time.
std::vector<Foo> v { Foo("A"),Foo("B"),Foo("C") };
Then you can access the objects by doing v[0]
. If you wanted to add another object to the vector
after initialization then you do v.push_back(Foo("D"))
C++ Vectors insert new objects
Calling c_str
on a std::string
returns a "live" pointer to the internal data stored in that std::string
instance. The pointer returned points to a buffer which is only valid as long as the std::string
on which it was called remains alive and unmodified.
The constructor of CExtracalModbusServer
just stores the pointer passed in. That pointer becomes dangling as soon as IP2
is re-assigned in the next iteration of the input loop. (The address pointed to is still the same, but the buffer which was previously located at that address has either been overwritten, or freed, or something else. In other words, the pointer is just dangling).
As for inserting into the vector: the first way (with insert
) could only work if the indices in the files are sequential and starting at 0
. You need a valid iterator into the vector to insert into it, where valid here means either pointing to one of the elements already in the vector, or the past-the-end iterator (the one returned by end()
). If INDEX
is equal to or larger than the size of the vector, then m_pServerCollection.insert(iterator + (INDEX), MBTemp);
will attempt to insert outside the vector (essentially a buffer overfow). Ubnefined behaviour ensues.
The push_back
way of inserting data should work, and if you see it misbehaving, it's either an artifact of the earlier bug (the one with dangling pointers), or there is a separate problem in code which you haven't shown.
Unrelated to the question at hand, but the code contains pretty bad practice in the form of manually managed dynamic memory. Instead of storing a CModbusServer*
in the vector and managing the memory manually with delete
in all the right places, you should be using std::unique_ptr<CModbusServer>
which will take care of proper deallocation for you, even in the presence of exceptions.
If CModbusServer
is under your control, you should likewise change it to store a std::string
instead of a const char*
. Never use C-style strings in C++ unless you have to interact with C-style APIs, and even in such case, limit their use only to the interaction itself. It's the same principle all over again: don't manage memory manually.
c++ adding objects to vector destroys earlier objects
Your A
class does not follow the Rule of Three:
The rule of three (also known as the Law of The Big Three or The Big Three) is a rule of thumb in C++ (prior to C++11) that claims that if a class defines one (or more) of the following it should probably explicitly define all three:
- destructor
- copy constructor
- copy assignment operator
These three functions are special member functions. If one of these functions is used without first being declared by the programmer it will be implicitly implemented by the compiler with the default semantics of performing the said operation on all the members of the class.
- Destructor – Call the destructors of all the object's class-type members
- Copy constructor – Construct all the object's members from the corresponding members of the copy constructor's argument, calling the copy constructors of the object's class-type members, and doing a plain assignment of all non-class type (e.g., int or pointer) data members
- Copy assignment operator – Assign all the object's members from the corresponding members of the assignment operator's argument, calling the copy assignment operators of the object's class-type members, and doing a plain assignment of all non-class type (e.g. int or pointer) data members.
The Rule of Three claims that if one of these had to be defined by the programmer, it means that the compiler-generated version does not fit the needs of the class in one case and it will probably not fit in the other cases either. The term "Rule of three" was coined by Marshall Cline in 1991.
An amendment to this rule is that if the class is designed in such a way that Resource Acquisition Is Initialization (RAII) is used for all its (nontrivial) members, the destructor may be left undefined (also known as The Law of The Big Two). A ready-to-go example of this approach is the use of smart pointers instead of plain ones.
Because implicitly-generated constructors and assignment operators simply copy all class data members ("shallow copy"), one should define explicit copy constructors and copy assignment operators for classes that encapsulate complex data structures or have external references such as pointers, if you need to copy the objects pointed to by the class members. If the default behavior ("shallow copy") is actually the intended one, then an explicit definition, although redundant, will be a "self-documenting code" indicating that it was an intention rather than an oversight.
You need to add a copy constructor and a copy assignment operator (and your destructor needs to use delete[]
instead of delete
):
class A
{
private:
int *array;
int size;
public:
A(int s)
: size(s), array(new int[s])
{
fprintf(stderr, "Allocated %p\n", array);
}
A(const A &src)
: size(src.size), array(new int[src.size])
{
std::copy(src.array, src.array + src.size, array);
fprintf(stderr, "Allocated %p, Copied from %p\n", array, src.array);
}
~A()
{
fprintf(stderr, "Deleting %p\n", array);
delete[] array;
}
A& operator=(const A &rhs)
{
A tmp(rhs);
std::swap(array, tmp.array);
std::swap(size, tmp.size);
return *this;
}
};
Since you mention emplace_back()
, that means you are using C++11 or later, which means you should also deal with move semantics of the Rule of Five:
With the advent of C++11 the rule of three can be broadened to the rule of five as C++11 implements move semantics, allowing destination objects to grab (or steal) data from temporary objects. The following example also shows the new moving members: move constructor and move assignment operator. Consequently, for the rule of five we have the following special members:
- destructor
- copy constructor
- move constructor
- copy assignment operator
- move assignment operator
Situations exist where classes may need destructors, but cannot sensibly implement copy and move constructors and copy and move assignment operators. This happens, for example, when the base class does not support these latter Big Four members, but the derived class's constructor allocates memory for its own use.[citation needed] In C++11, this can be simplified by explicitly specifying the five members as default.
You should add a move constructor and a move assignment operator to the above code:
class A
{
private:
int *array;
int size;
public:
A(int s)
: size(s), array(new int[s])
{
fprintf(stderr, "Allocated %p\n", array);
}
A(const A &src)
: size(src.size), array(new int[src.size])
{
std::copy(src.array, src.array + src.size, array);
fprintf(stderr, "Allocated %p, Copied from %p\n", array, src.array);
}
A(A &&src)
: size(0), array(nullptr)
{
std::swap(array, src.array);
std::swap(size, src.size);
fprintf(stderr, "Moved %p, Replaced with %p\n", array, src.array);
}
~A()
{
fprintf(stderr, "Deleting %p\n", array);
delete[] array;
}
A& operator=(const A &rhs)
{
A tmp(rhs);
std::swap(array, tmp.array);
std::swap(size, tmp.size);
return *this;
}
A& operator=(A &&rhs)
{
std::swap(array, rhs.array);
std::swap(size, rhs.size);
return *this;
}
};
Otherwise, you should strive for the Rule of Zero instead:
There's a proposal by R. Martinho Fernandes to simplify all of the above into a Rule of 0 for C++ (primarily for C++11 & newer). The rule of 0 states that if you specify any of the default members, then your class must deal exclusively with a single resource. Furthermore, it must define all default members to handle that resource (or delete the default member as appropriate). Thus such classes must follow the rule of 5 described above. A resource can be anything: memory that gets allocated, a file descriptor, database transaction etc.
Any other class must not allocate any resources directly. Furthermore, they must omit the default members (or explicitly assign all of them to default via
= default
). Any resources should be used indirectly by using the single-resource classes as member/local variables. This lets such classes inherit the default members from the union of member variables, thereby auto-forwarding the movability/copyability of the union of all underlying resource. Since ownership of 1 resource is owned by exactly 1 member variable, exceptions in the constructor cannot leak resources due to RAII. Fully initialized variables will have their destructors called & uninitialized variables could not have owned any resources to begin with.Since the majority of classes don't deal with ownership as their sole concern, the majority of classes can omit the default members. This is where the rule-of-0 gets its name.
Eliminate your manual array altogether and use a std::vector
instead:
class A
{
private:
std::vector<int> array;
public:
A(int s)
: array(s)
{
}
};
No need to explicitly define a copy/move constructor, copy/move assignment operator, or a destructor, because the default implementations provided by the compiler will automatically call the corresponding functionality of the vector
for you.
Adding an object to a vector
You've posted insufficient code (so your post is liable to be closed as off topic -- a classical beginners error on SO), but I guess the appropriate solution here is to use some auto pointer type. If your TrackVector
is to keep ownership of the Track
objects, then the best solution is have std::unique_ptr<Track>
elements:
std::vector<std::unique_ptr<Track>> TrackVector;
// filled like
TrackVector.emplace_back(new Track(args));
If, on the other hand ownership lies somewhere else, you may either use std::shared_ptr
or even raw pointers (provided your layout guarantees that the pointed to Track
objects' lifetime exceeds that of the TrackVector
).
C++: Adding automatically allocated objects to a std::vector
It appears that vector is making copies of existing objects when new ones are added
When you add an element, e.g. with v.push_back(AClass(i));
, what is done is a temporary AClass
object is created and passed to push_back
. push_back
must then copy this object into the container.
The other reason that you see copies made is that std::vector
stores its elements contiguously in an array. If there is no room left in the underlying array and you try to add another element to the end, the std::vector
must create a new array, copy the elements from the old array into a new one, and then insert the new element at the end. If you don't want this to occur, you can call std::vector::reserve
to reserve sufficient space in the std::vector
before you start inserting elements, or you can use a different sequence container, like std::deque
, which does not store its elements contiguously.
it seems like a lot of unnecessary allocation/deletion is taking place
In a C++ program, objects are frequently created and destroyed. Note that in your program, AClass
is very cheap to copy: its size is probably four or eight bytes, just large enough to hold its int
data member.
If you have a type that is expensive to copy (e.g., perhaps you have a large tree data structure that has thousands of nodes), then yes, copying may be too expensive. In that case, you can store smart pointers to dynamically allocated objects in the std::vector
instead (a std::vector<shared_ptr<AClass> >
, for example). If your compiler supports rvalue references and has a move-aware Standard Library implementation, you can make an expensive-to-copy type movable by implementing a move constructor and move assignment operator and using emplace_back
instead of push_back
.
why does the second version work when I haven't provided a copy constructor?
If you don't declare a copy constructor, the compiler provides a default copy constructor for you.
Related Topics
Delete All Items from a C++ Std::Vector
Friend Declaration Declares a Non-Template Function
When and Why Would You Use Static with Constexpr
How Do Static Variables in Lambda Function Objects Work
Load an Pem Encoded X.509 Certificate into Windows Cryptoapi
Check If One String Is a Prefix of Another
Libzip with Visual Studio 2010
Cannot Open Include File: 'Stdio.H' - Visual Studio Community 2017 - C++ Error
Qt: How to Handle the Event of the User Pressing the 'X' (Close) Button
How to Find the C++11 Standard Papers
Safely Override C++ Virtual Functions
Check the File-Size Without Opening File in C++
Checking If Two Cubic BéZier Curves Intersect
Using a Static Library in Qt Creator
C++ Object Created with New, Destroyed with Free(); How Bad Is This
How to Get the Icon, Mime Type, and Application Associated with a File in the Linux Desktop