This was the question: Write a program in C++ to enter two sequence separately and find total length of both sequences using constructor.
This is the program that I wrote. It runs but did I use constructors properly? Can we do this? Can we take user input in default constructors?
#include <iostream>
#include <string.h>
using namespace std;
class merge {
private:
int i;
char seq1[5];
int len, len1, len2;
char seq2[5];
public:
merge();
};
merge::merge() {
cout << "Enter first sequence: ";
cin >> seq1;
cout << "Enter second sequence: ";
cin >> seq2;
len1 = strlen(seq1);
cout << "Length of first sequence: " << len1 << "\n";
len2 = strlen(seq2);
cout << "Length of first sequence: " << len2 << "\n";
strcat (seq1, seq2);
cout << "Merged sequence: " << seq1 << "\n";
len = strlen(seq1);
cout << "Total length: " << len;
}
int main() {
merge o;
}
User input in default constructors (C++). Can this be done?
Technically yes. Constructor body is mostly the same as any other function, and I/O is possible.
Practically, I/O in a constructor would be a bad design.
So we can, but we do not do this.
Can we take user input in default constructors?
Yes you can do it, but you shouldn't.
There are many ways to explain why. For example, consider what it means to be default-constructible. From cppreference about the named requirement DefaultConstructible:
Specifies that an instance of the type can be default constructed.
Requirements
The type T satisfies DefaultConstructible if
Given
u, an arbitrary identifier
The following expressions must be valid and have their specified
effects
T u The object u is default-initialized
T u{} The object u is value-initialized or aggregate-initialized.
T() / T{} A temporary object of type T is value-initialized or aggregate-initialized.
Your merge is DefaultConstructible. But is it really?
Consider what happens when you add a second constructor:
merge::merge(std::string seq1, std::string seq2);
Now I can choose whether I want to use the default constructor to take input from user, or get the sequences from elsewhere and call the other constructor. Sounds good, no?
(It isn't. It just helps me to emphasize the problem with your default constructor in the following examples. Bear with me.)
Code that is fine with a default constructible type T, but not quite with your merge is...
example std::vector
using T = merge;
std::vector<T> v(10);
v[0] = T("1,2,3,4","4,5,6,7,8");
// ...
Because I want to avoid reallocations of the vector and because I know that T can be default constructed, I created a vector with 10 elements even though I want to create the actual elements only later. I didn't want to ask for user input 10 times when I wrote std::vector<T> v(10); but thats the only way to resize a vector that contains elements of your class.
example: std::map
std::map<int,T> m;
m[1] = T("1,2,3,4","4,5,6,7,8");
I want to add an element to the map (if it didnt exist before). std::map::operator[] tries to find an element with the given key (1). It does not find one, hence it inserts a new element with that key and a default constructed T. Then it returns a reference to that T.
I could have used insert, but I was lazy and used []. As a consequence, this code asks the user for input, but thats not what I wanted.
Here is a demo of the two issues I tried to explain above: https://godbolt.org/z/fj5qoa
Formally, your merge is default-constructible, but for most practical purposes it isn't. A default constructor is expected to construct an object in a default state. Reading user input is surprising and many types and algorithms that require that your type is default constructible will not work as expected, even though you provided a default constructor.
Related
The code
using namespace std;
class A
{
private:
vector<int> a;
public:
A(vector<int> x):a(x){}
string toString()
{
string s;
for (auto& element : a)
{
s += to_string(element) + " ";
}
return s;
}
};
int main()
{
A a1({1,2,3});
A a2({11,12,13});
cout << "a1 = " << a1.toString() << "\n";
cout << "a2 = " << a2.toString() << "\n";
swap(a1,a2);
cout << "a1 = " << a1.toString() << "\n";
cout << "a2 = " << a2.toString() << "\n";
return 0;
}
outputs as expected
a1 = 1 2 3
a2 = 11 12 13
a1 = 11 12 13
a2 = 1 2 3
From cplusplus.com > std::swap under complexity
Non-array: Constant: Performs exactly one construction and two assignments (although notice that each of these operations works on its own complexity).
Array: Linear in N: performs a swap operation per element.
Does it mean that std::swap when applied to a1 and a2 only swaps the pointers to the arrays [1,2,3] and [11,12,13] but does not copy any int or anything else?
What exactly does std::swap does when applied to two objects of the class A?
Assuming the std::swap copies all elements of the array, should I write a static A::swap function, using vector::swap whose time complexity is constant (from cplusplus.com > vector::swap) meaning that it only swaps the pointers?
[..] want to add a note that the semantics of std::swap is changed in C++17. So it might be a good idea to mention compiler, version of it and what standard you target.
I was hoping that a simple looking question would not have brought about complexities about C++ standards and compiler versions. I typically compile my code in C++11. For the sake of completeness here is the gcc version on my laptop.
$ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/usr/include/c++/4.2.1
Apple LLVM version 9.0.0 (clang-900.0.39.2)
Target: x86_64-apple-darwin17.3.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
There is a requirement that the type parameter to the std::swap template
is MoveConstructible and MoveAssignable. This suggests that the swap can be written roughly as (a few bits omitted)
void swap(T &a, T &b) {
T tmp{std::move(a)};
a = std::move(b);
b = std::move(tmp);
}
For your example class, it'll invoke the default move ctor/move assignment operators (edit: of A) a few times and they in turn will invoke those of std::vector.
IOW, you can expect your program to be reasonably efficient as is.
Alternatively, you can define a non-member swap function in the same namespace as A and call explicitly std::swap with vector parameters. Or call directly std::vector::swap.
Depending on your compiler and the classes/types you are using the swap function will either copy one of the classes, or use the move constructor/assignment (C++11 and upwards) (http://en.cppreference.com/w/cpp/utility/move).
Your class only consists of a vector and for vector classes this move constructor does what you refer to as simply "swapping pointers", so it will be very quick. Just be aware that implementing e.g. a copy constructor will delete the implicitly-declared move constructor which makes this 'magic' happen!! (http://en.cppreference.com/w/cpp/language/move_constructor#Implicitly-declared_move_constructor)
While looking at the swap function independently, I'm giving you an avalanche of followup-insights:
One of the most famous uses of the swap-function happens in the copy-and-swap idiom (https://progdoo.wordpress.com/2012/06/03/c11-copy-and-swap-idiom/). This works most efficiently with implemented move-constructors/assignments.
I created a map of type map<T, const T&>. For current example purpose, let say T is:
class Bar {
public:
Bar(int x) {this->x = x;}
int x;
};
Next I create a map and insert Bar keyed with some integers.
Bar bs[] = {Bar(1), Bar(2), Bar(3)};
map<int, const Bar&> my_map;
for (int i = 0; i < 3; i++) {
const Bar &b = bs[i];
cout << "Setting map." << i
<< " with x = " << b.x << endl ;
my_map.insert(std::make_pair(i, b));
}
So far everything looks good, and b.x prints the values 1; 2; 3 as expected. Next we retrieve these values back.
for (int i = 0; i < 3; i++) {
auto iter = my_map.find(i);
if (iter == my_map.end()) {
cout << "Not found!" << endl;
continue;
}
cout << "map." << i << " = " << iter->second.x << endl;
}
The output prints the last value each time as shown below.
// map.0 = 3
// map.1 = 3
// map.2 = 3
And that's what is confusing to me, as I expect 1; 2; 3. If I replace value type of map with just const Bar it gives 1; 2; 3. I've been trying to make sense out of it, but so far it just looks like undefined behaviour to me. The wildest explanation I can imagine is that &b is like a box storing pointer to the object, and the box ends up being shared across loop, and make_pair uses &b as a box value than like a pointer/reference (and hence explains the last value being printed).
Edit: I understand it may not be good idea to use map like this, but I'm curious why this is happening than what should I be using instead. As in semantically, what did I miss when I wrote this and why it went through compiler, or why compiler made whatever assumption it made.
Edit: Example on repl.it running the code: https://repl.it/repls/IgnorantExhaustedBluejay
Essentially the same problem as here: How can I have a pair with reference inside vector?
Your call to std::make_pair creates a temporary std::pair object that does not have a reference as its second member. The second member of the pair is a regular value of type Bar. Meanwhile, your map stores references. The reference gets bound to the second member of the temporary created by std::make_pair. Later the temporary gets destroyed. The reference becomes dangling.
Each temporary on each iteration of the cycle is apparently created at the same location in memory. So, all these dangling references in your map refer to the same location in memory. Which just happens to hold the residual value of 3 at the time of printing. That explains the output.
A map with raw references is not a very good idea. But if you want to somehow force it to work with raw references, stop using std::make_pair. Instead, manually construct a proper std::pair, making sure to explicitly specify the proper types
my_map.insert(std::pair<const int, const Bar &b>(i, b));
Or you can keep using std::make_pair as follows
my_map.insert(std::make_pair(i, std::cref(b)));
But switching entirely to std::reference_wrapper and std::cref is a better idea.
P.S. BTW, in C++17 mode GCC refuses to compile the code with raw references. C++14 mode does compile it.
I wasn't even aware that it's possible to have a map of references
You should probably simply store the object you want directly :
map<int, Bar> my_map;
If you want the "Bar"s objects to live outside the map, you should use pointers instead of references. Just be sure you don't destruct the Bar objects without removing them from the map :
map<int, Bar*> my_map;
my_map[2] = &bs[0];
and then:
int x = my_map[2]->x;
Edit
I think the map is holding a reference to the temporary pair. You can see this in debug if you extract the creation of the pair :
auto tempPair = std::make_pair(i, b);
my_map.insert(tempPair);
Then after adding bs[0] if we run the creation of the pair, the value of my_map[0] change even before adding the second one:
This makes it work:
my_map.insert(std::make_pair(i, std::reference_wrapper<const Bar>(b)));
I am coming from a C#/Java background into C++, using visual studio community 2017 & plenty of tutorials. I came to the point where am unsure of what is a correct way to write a function to process a vector of data. Should I force a function to use a pointer / reference? Should I let compiler sort it out? What is best practice?
This is my main, I ask for an input on vector size, then pass a pointer to the integer value to function that creates and populates vector with values through a simple for loop.
I then pass the array to another function that performs a shuffle.
vector<int> intVector(int* count)
{
vector<int> vi;
for (int i = 1; i <= *count; i++)
vi.push_back(i);
return vi;
}
vector<int> &randVector(vector<int> *v)
{
shuffle(v->begin(), v->end(), default_random_engine());
return *v;
}
int _tmain(int argc, _TCHAR* argv[])
{
int count;
cout << "Enter vector array size: ";
cin >> count; cout << endl;
cout << "Vector of integers: " << endl;
vector<int> vi = intVector(&count);
for_each(vi.begin(), vi.end(), [](int i) {cout << i << " ";});
cout << endl;
vi = randVector(&vi);
cout << "Randomized vector of integers: " << endl;
for_each(vi.begin(), vi.end(), [](int i) {cout << i << " ";});
cout << endl;
return 0;
}
So my question is, what is the best practice in my case to avoid unnecessary copying. Should I even care about it? Should I rely on compiler to solve it for me?
I am planing to use C++ for game development on desktop and consoles. Understanding memory and performance management is important for me.
You are in charge of enforcing (or avoiding) the copy of objects around.
Regarding your example:
You can avoid using pointers and use a reference instead.
Like in the following:
vector<int>& randVector(vector<int>& v)
{
shuffle(v->begin(), v->end(), default_random_engine());
return v;
}
Note that since you are using a reference, the shuffle operation is already modifying the parameter of randVector so there is no real need to return a reference to it.
As a rule of thumb when you need to pass an object around and you want to avoid a potentially expensive copy you can use references:
void function(<const> Object& v)
{
// do_something_with_v
}
The rules on passing in C++ for typical code are pretty straightforward (though obviously still more complex than languages without references/pointers).
In general, prefer references to pointers, unless passing in null is actually something you might do
Prefer to write functions that don't mutate their inputs, and return an output by value
Inputs should be passed by const reference, unless it is a primitive type like an integer, which should be passed by value
If you need to mutate data in place, pass it by non-const reference
See https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rf-conventional for more details.
The upshot of this is that here are the "correct" signatures for your two functions:
vector<int> intVector(int count);
void randVector(vector<int> &v);
This doesn't take into account iterators which is probably really the correct "generic" way to write the second function but that is a bit more advanced. But, see std::shuffle which lets you randomize any arbitrary container by leveraging iterators: http://en.cppreference.com/w/cpp/algorithm/random_shuffle.
Since you mentioned unnecessary copying, I will mention that when you return things like vector by value, they should never be copied (I'm assuming you're using C++11 or newer). They will instead be "moved", which doesn't have significant overhead. Thus, in newer C++ code, "out parameters" (passing in arguments by reference to mutate them) is significantly discouraged compared to older versions. Good to know in case you encounter dated advice. However, passing in by reference for something like shuffling or sorting is considered an "in/out" parameter: you want to mutate it in place and the existing data is important, not simply being overwritten.
I have a C++ program in which I want to insert default values for any keys missing in a std::map. I'm thinking the easiest way to do this would be to use std::map::operator[]() like the POSIX touch command - that is, to leave the value unchanged if it already exists, but to create it if it doesn't. For example,
#include <map>
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> keys = {0, 1};
map<int, int> m;
m[1] = 5;
m[2] = 12;
for (const int i : keys)
{
m[i]; // touch value
}
for (auto const & kv : m)
{
cout << kv.first << ", " << kv.second << endl;
}
}
Can I be sure that the compiler won't optimize out the m[i]; statements, since I'm not "doing" anything with them? (Not explicitly assigning to, not reading from.)
Yes you can be sure. Optimizing the call away would change the observable behavior of your program, and the compiler is not allowed to do this (except in the case of RVO).
This is known as the as-if rule.
Yes, you can be sure. It's perhaps more intuitive when you consider that the line in question is equivalent to this:
m.operator[](i);
…and you don't expect arbitrary function calls to be optimised out of your program, if they do anything.
The [] operator does indeed default-construct the value that would be at that key's location if you do not assign it something.
Reference Link
If k does not match the key of any element in the container, the
function inserts a new element with that key and returns a reference
to its mapped value. Notice that this always increases the container
size by one, even if no mapped value is assigned to the element (the
element is constructed using its default constructor).
I'm trying to insert a copy of an existing vector element to double it up. The following code worked in previous versions but fails in Visual Studio 2010.
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char* argv[])
{
vector<int> test;
test.push_back(1);
test.push_back(2);
test.insert(test.begin(), test[0]);
cout << test[0] << " " << test[1] << " " << test[2] << endl;
return 0;
}
Output is -17891602 1 2, expected 1 1 2.
I've figured out why it's happening - the vector is being reallocated, and the reference becomes invalid before it's copied to the insertion point. The older Visual Studio apparently did things in a different order, thus proving that one possible outcome of undefined behavior is to work correctly and also proving that it's never something you should rely on.
I've come up with two different ways to fix this problem. One is to use reserve to make sure that no reallocation takes place:
test.reserve(test.size() + 1);
test.insert(test.begin(), test[0]);
The other is to make a copy from the reference so that there's no dependency on the reference remaining valid:
template<typename T>
T make_copy(const T & original)
{
return original;
}
test.insert(test.begin(), make_copy(test[0]));
Although both work, neither one feels like a natural solution. Is there something I'm missing?
The issue is that vector::insert takes a reference to a value as the second parameter and not a value. You don't need the template to make a copy, just use a copy constructor to create another object, which will be pass by reference. This copy remains valid even if the vector is resized.
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char* argv[])
{
vector<int> test;
test.push_back(1);
test.push_back(2);
test.insert(test.begin(), int(test[0]));
cout << test[0] << " " << test[1] << " " << test[2] << endl;
return 0;
}
I believe this is defined behavior. In §23.2.3 of the 2011 C++ standard, table 100 lists sequence container requirements and there is an entry for this case. It gives the example expression
a.insert(p,t)
where a is a value of X which is a sequence container type containing elements of type T, p is a const iterator to a, and t is an lvalue or const rvalue of type X::value_type, i.e. T.
The assertion for this expression is:
Requires: T shall be CopyInsertable into X. For vector and deque, T shall also be CopyAssignable.
Effects: Inserts a copy of t before p.
The only relevant vector specific quote I could find is in §23.3.6.5 paragraph 1:
Remarks: Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.
Although this does mention the vector being reallocated, it doesn't make an exception to the previous requirements for insert on sequence containers.
As for working around this issue, I agree with #EdChum's suggestion of just making a copy of the element and inserting that copy.