I am learning to use C++ vectors, and I can't quite understand the output of the following program:
#include <iostream>
#include <vector>
using namespace std;
class Custom {
public:
int v;
Custom() = delete;
explicit Custom(int v) : v{v} {};
Custom(const Custom &) : v{4} {
}
friend ostream &operator<<(ostream &os, const Custom &th) {
os << "V is " << th.v << endl;
return os;
}
};
int main(int argc, char *argv[]) {
vector<Custom> c(2, Custom(3));
c[0].v = 5;
for (auto i: c) {
cout << i << endl;
}
}
I expected it to produce the output
V is 5
V is 4
But instead it produces
V is 4
V is 4
Am I missing something obvious? Thanks.
This range based loop is making copies:
for (auto i: c) {
cout << i << endl;
}
And the copy constructor initializes v to 4 (and does not make a copy):
Custom(const Custom &) : v{4} {
}
You can either implement a proper copy constructor or use references in the loop to get the desired output:
for (const auto& i: c) {
cout << i << endl;
}
I would suggest to do both, because this copy constructor is not doing a copy by any means. The compiler generated copy constructor should be fine:
Custom(const Custom &) = default;
PS: The fact that Custom has a deleted default constructor is not really relevant for the posted code. Nowhere in the code a Custom is default constructed. Also there is no iterator in your code. In the range based loop i is a copy/reference of the elements in the vector, it is not an iterator.
When you wrote:
for (auto i: c) //this uses copy constructor to copy initialize each element one by one from the vector
{
}
In the above snippet, each individual element of the vector is used to copy initiaize a temporary object named i while iterating through the vector. And since you have the v{4} in the constructor initializer list of the copy constructor you get the mentioned output.
To solve this you should replace: auto i: c with auto &i: c or const auto &i: c as shown below:
for (const auto &i: c)
{
cout << i << endl;
}
Now the elements of the vector are not copied into i. Instead they are references to the object themselves and so you'll get your expected output.
Related
Consider the following code .
#include <iostream>
#include <map>
class A{
public:
int m_id;
int m_x;
int m_y;
A() = default;
A(int i, int j, int k) : m_id(i), m_x(j), m_y(k){}
};
void add(const A& a, std::map<int,A>& m){
m[a.m_id] = a;
}
void modify(std::map<int,A>& m){
for(auto& x : m){
x.second.m_x*=10;
x.second.m_y/=10;
}
}
void print(std::map<int,A>& m){
for(const auto& x : m){
std::cout << x.first << " : (" << x.second.m_x << ',' << x.second.m_y << ")\n";
}
std::cout << '\n';
}
int main(int argc, char const *argv[])
{
std::map<int,A> m;
A a(1,10,100);
add(a,m);
print(m);
{
A b(2,10,200);
add(b,m);
print(m);
}
modify(m);
print(m);
return 0;
}
I am creating a std::map of objects of type A. In add, I add an object to the std::map by using a const reference to that object. In modify, I am modifying values of std::map. In print, I am printing the map. Basically accessing the elements.
I add b in a different scope to test if it I get a memory error.
The code works correctly.
My question is, is std::map with [] operator creating a new non const copy of the object ? Also, std::vector::push_back works fine. Does push_back also create a copy. I am not calling emplace.
Yes, a new object will be constructed as the element of std::map. To be precise, in m[a.m_id] = a;, if the key (i.e. a.m_id) does not already exist, a value-initialized A will be constructed as the new element of m firstly, then m[a.m_id] returns the reference to value of the new element; which is copy-assigned from a via the copy-assignment operator of A later.
If the key (i.e. a.m_id) already exists, m[a.m_id] just returns the reference to value of the existed element; which is copy-assigned from a then.
I'm writing some codes in which there are 2 vectors containing 4 smart pointers respectively. I accidentally apply an iterator generated in the first vector to the erase method in the second vector. Then the program crashes. I learn that the copy construction and the move construction get involved the erase method. In light of the debugger, I figure out 1) a nullptr and 2 smart pointers stay in 1st vector. 2) 4 smart pointers reside in 2nd vector. 3) the program starts to crash after several successful run. My questions are as follows,
how is the nullptr appended to 1st vector?
Why is it permissible to apply the iterator to 2nd vector?
Why does the program not crash from the outset?
BTW, my platform is Xcode 8.1. Thanks in advance
#include <memory>
#include <vector>
#include <iostream>
#include <string>
using namespace std;
class A{
public:
A(string name_) : name(name_) {cout << name << " construction\n";}
const string& get_name() const {return name;}
~A() {cout <<get_name() << " destruction\n";}
A (const A& rhs) : name(rhs.name){cout << "A copy constructor\n";}
A(A&& rhs) : name(""){
cout <<"A move constructor\n";
swap(rhs);
}
void swap(A& rhs) noexcept {
std::swap(name, rhs.name);
}
private:
string name;
};
void foo();
int main(){
foo();
}
void foo(){
vector<shared_ptr<A>> vect1, vect2;
auto a1 = make_shared<A>("Mike");
auto a2 = make_shared<A>("Alice");
auto a3 = make_shared<A>("Peter");
auto a4 = make_shared<A>("Paul");
vect1.push_back(a1);
vect1.push_back(a2);
vect1.push_back(a3);
vect1.push_back(a4);
vect2.push_back(a4);
vect2.push_back(a1);
vect2.push_back(a2);
vect2.push_back(a3);
auto it = vect1.begin();
vect1.erase(it);
for (auto &c : vect1){
cout << c->get_name() << endl;
}
vect2.erase(it);
for (auto &c : vect2){
cout << c->get_name() << endl;
}
}
In VS2015 it fails on line vect2.erase(it); with the message; Iterator out of bounds which indeed it is.
As you state in the question it doesn't even belong to vect2.
Even if it works on your platform it is undefined behavior. So from then on anything can happen.
You're not working within the c++ standard anymore, you're now dealing with whichever way your platform was implemented (maybe it uses pointers for iterators, maybe it uses offsets; who knows?).
// Example program
#include <iostream>
#include <ostream>
#include <string>
#include <vector>
class One
{
public:
One(int age, int price)
: m_age(age), m_price(price)
{
std::cout << "m_age: " << m_age << " , m_price: " << m_price << std::endl;
}
One(const One&) = default;
One& operator=(const One&) = default;
int age() const { return m_age; }
int price() const { return m_price; }
private:
int m_age;
int m_price;
};
std::ostream& operator<<(std::ostream& os, const One& one)
{
os << "<< m_age: " << one.age() << " , m_price: " << one.price();
return os;
}
int main()
{
std::vector<One> vecOnes = {{1, 2}, {3, 4}};
//for(auto it: vecOnes) // case I
//for(auto& it: vecOnes) // case II
for(const auto& it: vecOnes) // case III
{
std::cout << it << std::endl;
}
}
All three cases output the same results as follows:
m_age: 1 , m_price: 2
m_age: 3 , m_price: 4
<< m_age: 1 , m_price: 2
<< m_age: 3 , m_price: 4
Question> which case is more efficient way to use auto?
Originally, I expect the auto will trigger the constructor of class One. But it doesn't show that way based on the output results.
Originally, I expect the auto will trigger the constructor of class One. But it doesn't show that way based on the output results.
It does trigger a constructor: the copy constructor. But you didn't instrument that one† so you don't see any output. The other two cases don't construct a new object, so will definitely be more efficient.
Note that there is also a fourth case for (auto&& it : vecOnes) {...} It will be equivalent to your second case here and also not create any new objects.
†Well now that you edited your question, it should be pretty clear that the one case does construct new objects and the others all do not.
The reality is, it depends...
If you've just got a vector of a built-in type, such as an int then you might as well use auto it as it's cheap to make a copy and you'll save a dereference every time you use the value. For classes this will invoke the copy constructor which may affect performance.
However, if you're vector is contains a class then in general it'll be more efficient to use auto &it or const auto &it in order to save creating a copy of the object. There's no cost advantage to using const over non-const, it just comes down to how you want to interact with the object.
I am reading about the new C++11 syntax for iterating over STL containers.
So far I came across the following example:
std::vector<int> plus;
....
for(int l : plus)
{
std::cout << l;
}
My question is does the above syntax make a copy of the int ?
If so wouldnt this be more efficient ?:
for(int& l : plus)
Semantically, it makes a copy, although for built-in types there may be no efficiency hit, in fact it may even be cheaper to use a copy. But if you have expensive to copy objects, it is a better idea to use const references in the loop.
std::vector<ExpensiveToCopy> v;
for (const auto& i : v)
std::cout << i << std::endl;
You should only really use non-const references if you want to mutate the object.
Yes, if you don't explicitly say you want a reference, you get a copy. With built-in types, its actually more efficient to take a copy - unless you want the semantics of references, of course.
It will call the copy constructor for each element in the vector. If you take it by const reference, it doesn't call any constructors at all. You should use const if you don't plan on mutating the elements. For example:
class Test
{
public:
Test() { std::cout << "Default.\n"; }
~Test() { }
Test(const Test& other) { std::cout << "Copy.\n"; }
Test(Test&& other) { std::cout << "Move.\n"; }
};
int main()
{
std::vector<Test> test;
test.emplace_back(Test());
for (const Test& t : test)
{
}
}
I was going through Josuttis's "Using Map's as associative arrays" (from The C++ Standard Library - A Tutorial and Reference, 2nd Edition) and came across Using a std::map as an associative array on Stack Overflow. Now I have more questions on the constructors that are called when inserting into a map.
Here is my sample program (not using best coding practices; please excuse me for that):
class C
{
public:
string s;
C() { cout << "default " << endl;}
C(const string& p) : s(p)
{ cout << "one param" << endl;}
C(const C& obj)
{
if (this != &obj)
{
s = obj.s;
}
cout << "copy constr" << endl;
}
C& operator = (const C& obj)
{
if (this != &obj)
{
s = obj.s;
}
cout << "copy initializer" << endl;
return *this;
}
};
int main()
{
map<int,C> map1;
C obj("test");
cout << "Inserting using index" << endl;
map1[1] = obj;
cout << "Inserting using insert / pair" << endl;
map1.insert(make_pair(2,obj));
}
The output for this program is:
one param
Inserting using index
default
copy constr
copy constr
copy initializer
Inserting using insert / pair
copy constr
copy constr
copy constr
copy constr
I was assuming that initializing the map by index should call the default constructor and followed by the assignment operator.
But executing map1[1] = obj creates following output;
Inserting using index
default
copy constr
copy constr
copy initializer
Can someone help me to understand the initialization better?
If you read the specification for std::map, it says that operator[] is equivalent to (in this case)
(*((this->insert(make_pair(1,C()))).first)).second
So this explains all the constructor calls you see. First it calls the default constructor C(). Then it calls make_pair, which copies the C object. Then it calls insert, which makes a copy of the pair object you just made, calling the C copy constructor again. Finally it calls the assignment operator to set the inserted object to the one you are assigning it to.
Don;t know.
But this is interesting:
#include <string>
#include <map>
#include <iostream>
using namespace std;
class C
{
public:
string s;
C()
{
cout << "default " << endl;
}
C(const string& p)
: s(p)
{
cout << "one param(" << s << ")" << endl;
}
C(const C& obj)
:s(obj.s)
{
cout << "copy constr(" << s << ")" <<endl;
}
C& operator = (const C& obj)
{
cout << "copy initializer\t" <<;
C copy(obj);
std::swap(s,copy.s);
return *this;
}
};
int main()
{
map<int,C> map1;
cout << "Inserting using index" << endl;
map1[1] = C("Plop");
}
It looks like the default one is created and copied around.
Then the external one is just assinged over it once it has been put in place.
Inserting using index
default
copy constr()
copy constr()
one param(Plop)
copy initializer copy constr(Plop)
What happens if you simply execute map[1];? This may involve internal copies, depending on the implementation of map your standard library uses.
Actually map1[1] = obj will create pair first