Pass by value, no deep copy on assignment with std::vector? - c++

Understanding std::set.insert & std::vector behavior.
Please consider the following scenario:
A.h
class A {
uint id;
vector<double> values;
operator<(const A& argA) const;
}
A.cpp
A::A(uint argId, vector<double> argValues) {
this->id = argId;
this->values = argValues;
}
A::operator<(const A& argA) const {
// it's guaranteed that there's always at least one element in the vector
return this->values[0] < argA.values[0];
}
B.cpp
std::set<A> mySet;
for (uint i = 0; i < (uint) 10; i++)
{
vector<double> tempVector(3);
for (uint j = 0; j < (uint) 3; j++) {
tempVector[j] = j;
}
myset.insert(A(i + 1, tempVector));
}
In my understanding, tempElement owns a deep copied vector (values), because the vector was passed by value in its constructor and assigned. Therefore looping over i shouldn't break the added elements to my set. BUT inserting *tempElement breaks - SIGSEV. In my logic this should work... Every help appreciated!
EDIT: the code crashes during the insertion process (second element); set invokes the LT-operator, tries to access the vector of the passed argument - but cannot. Before the creation of A where I pass the id and the vector I check if the passed vector contains the right elements.

For a small vector it shouldn't matter, but if you have a large array and it will be expensive to keep copying it, yourA should contain some kind of pointer that shallow-copies. There are several options:
boost::shared_array<double>
boost::shared_ptr<vector<double> >
boost::shared_ptr<double> but with array deleter passed in on construction.
Make A non-copyable and have a set of (shared) pointers to A with some comparison functor that compares what is in the pointers rather than the pointers themselves.
Note that with either shared_array or shared_ptr you won't be able to extract the size (number of elements) so you would have to store that separately.

I don't think the problem is in this code. However I notice you have a vector tempVector but you assing the values to tempComponents instead. I can't see tempComponents declaration but my guess is it is of different size.

Working code with numerous changes - but I don't see the problem that you were describing.
#include <set>
#include <vector>
using namespace std;
typedef unsigned int uint;
class A {
public:
A(uint argId, vector<double> argValues)
{
this->id = argId;
this->values = argValues;
}
bool operator < ( A const& a ) const
{
return a.id < id;
}
uint id;
vector<double> values;
};
int _tmain(int argc, _TCHAR* argv[])
{
std::set<A> mySet;
for (uint i = 0; i < (uint) 10; i++)
{
vector<double> tempVector(3);
for (uint j = 0; j < (uint) 3; j++) {
tempVector[j] = j;
}
std::unique_ptr<A> tempElement(new A(i + 1, tempVector));
mySet.insert(*tempElement);
}
return 0;
}

No, there's no reason for inserting into myset here to cause a crash. The problem must lie elsewhere. Perhaps in A's copy ctor if you're not using the default one.
However your code is leaking memory. When you insert into the set *tempElement is copied into the set, and then the original that you allocated with new is no longer used but is never deleted. Instead you could just do A tempElement(i+1,tempVector); so that after the object is copied into the set it gets properly destroyed. Or perhaps better in this case you could just construct it as a temporary passed directly to insert: myset.insert(A(i+1,tempVector)) in which case the object will be moved instead of copied, reducing the overhead. Or you could just construct the object in place to avoid even moving: myset.emplace(i+1,tempVector);
Also I'm assuming that by tempComponents[j] = j; you meant tempVector[j] = j. You could replace that loop with std::iota(begin(tempVector),end(tempVector),0). edit: or you could use the new initializer syntax Furthermore, since the vector is the same everytime you could use just one outside the loop:
vector<double> tempVector(3) = {0.0,1.0,2.0}
std::set<A> mySet;
for (uint i = 0; i < (uint) 10; i++)
{
myset.emplace(i+1,tempVector);
}
C++03 compilers won't support emplace or the new initializer syntax, and iota would be a compiler extension for them (it's from the original SGI STL, so some may have it). For those you would still use insert and use a for loop to initialize tempVector or use an array:
double tempVector_init[] = {0.0,1.0,2.0};
vector<double> tempVector(tempVector_init,tempVector_init+3);
std::set<A> mySet;
for (uint i = 0; i < (uint) 10; i++)
{
myset.insert(A(i+1,tempVector));
}

Related

Choose at runtime array or vector in C++

I have a problem described as below ::
class datad {
private:
int *a;
int _size;
vector<int> v;
public:
datad(int arr[], int size) {
_size = size;
for (int i = 0; i < size; i++)
a[i] = arr[i];
}
datad(vector<int> ve)
{
v = ve;
_size = ve.size();
}
void printdata()
{
// print data which the object has been initialized with
// if object is initialized with vector then print vector
// array print array
}
};
int main()
{
// print only vector data
int a[] = { 9,4,8,3,1,6,5 };
datad d(v1);
d.printdata();
// print only array data
datad d1(a, 7);
d1.printdata();
}
I need to find the way the object is initialized and then based on the same should be able to printdata accrodingly.
Can someone help me understand if it is possible at all?
Add a bool usesVector to your class and set it to true or false in each constructor as appropriate. Then, in printdata, simply check the value of the boolean.
Or you can set size to -1 in the vector case (as it's otherwise unused) and just check for that.
By the way, your array implementation is broken, because you never allocate any memory for it. You'd be much better off using only the vector version. You can still initialise that vector from array data if you wish.
You can set a flag in respective constructor and check that flag during the printing method.
I hope this is for learning purposes, otherwise as noted you maybe better of using just the vector version. When using dynamic memory management in class you need to be aware of things like rule of three and I guess there is also rule of five.

Filling array of pointers by function c++

Hello I have made class gabka and a function f1 to which I would like to pass an array of pointers to fill this array with gabka objects but I get weird error. How to correct it?
error:
cannot convert from gabka to int
the code :
#include <iostream>
using namespace std;
const int n = 4;
class gabka {
public:
float woda;
gabka(){
woda = 0;
}
void f1(gabka t[n]){
for(int i = 0; i < n; i++){
t[i] = new gabka();
}
}
};
int main() {
gabka** t = new gabka*[n];
return 0;
};
Your f1 function takes an array of gabka objects, and you are trying to assign a gabka* to each one of them. You need to decide what you want to pass to f1. For example,
void f1(gabka t[n]){
for(int i = 0; i < n; i++){
t[i] = gabka(); // assign default constructed gabkas
}
}
or
void f1(gabka* t[n]){
for(int i = 0; i < n; i++){
t[i] = new gabka();
}
}
In the latter case, bear in mind you have to call delete on all the elements of t.
It isn't clear what you are intending to do with your code, but, as a general rule, in C++ you should avoid raw pointers and manual dynamic allocation. Prefer standard library containers over dynamically allocates arrays or arrays of dynamically allocated objects.
t[i] = new gabka();
t is an array of gabka, not an array of gabka*.
Either change the declaration of t to be an array of gabka*, or fill it with gabka instances.

How to solve the issue passing an unknown-sized 2D array to a function?

I have a program in which the object array's size is determined during the runtime, so it's dynamically allocated (2D array, read from file). I also have a function which takes these objects as parameters. The problem is if the function parameters are 2D arrays that are passed to the function the 2nd dimension should be determined. However, in my case it is not. My program won't compile since the prototype does not have the 2nd dimension mentioned.
Here is what I tried:
//global variables
int residentCount=0;
int hospitalCount=0;
Resident** residents;
Hospital** hospitals;
bool readFromFiles(const string, const string, const int); //sizes are determined in here
void print(Hospital*[hospitalCount], Resident*[residentCount]); //declaration issue
How can I solve this?
You are programming in C++, so you should:
avoid dynamic allocation and handling memory management on your own always when it's possible
take advantage of objects with automatic storage duration instead, follow RAII idiom
avoid using C-style arrays and actually avoid writing C code that is just compilable as C++ in general
use great features that C++ provides, especially those bundled within STL
avoid using global variables when local equivalents suffice
This is how it could look like:
typedef std::vector<Resident> Residents;
typedef std::vector<Hospital> Hospitals;
// passing by const reference:
void print(const std::vector<Hospitals>&, const std::vector<Residents>&);
int main()
{
std::vector<Hospitals> hospitals;
std::vector<Residents> residents;
...
} // <-- lifetime of automatics declared within main ends here
Note that hospitals and residents will be objects with automatic storage duration, usable in similar manner than your C-style 2D arrays. When the execution goes out of the scope of main, these vectors are destructed and memory, where their elements (including elements of their elements) resided before is automatically cleaned up.
Also note that I suggest you to pass by const reference, i.e. const std::vector<Hospitals>&, which prevents the copy of passed object being created and const keyword explicitely tells to the caller: "Although you pass this object by reference, I will not change it."
Just pass a pointer to the first element of the array and the dimensions, that's enough, example:
void PrintHospitals(Hospital* Hospitals, size_t HospitalRows, size_t HospitalColumns)
{
size_t i, j;
Hospital* hospital;
for (i = 0; i < HospitalRows; i++)
for (j = 0; j < HospitalColumns; j++)
{
hospital = Hospitals + HospitalColumns * i + j;
PrintHospital(hospital);
}
}
int main()
{
Hospital hospitals[10][20];
// ...
PrintHospitals(&hospitals[0][0], 10, 20);
return 0;
}
Here is a solution using templates to create two-dimensional array wrappers for existing data:
template<typename T>
class Array2d {
public:
int Rows;
int Cols;
T** Data;
Array2d(int rows, int cols, T** data) :
Rows(rows),
Cols(cols),
Data(data) { }
};
void print(Array2d<Hospital> h, Array2d<Resident> r) {
for (int i = 0; i < h.Rows; i++) {
for (int j = 0; j < h.Cols; j++) {
//Print Data[i][j] element here
}
}
// Other print code
}
int main()
{
Resident** residents;
Hospital** hospitals;
//Init data arrays
Array2d<Hospital> h(10, 10, hospitals);
Array2d<Resident> r(10, 10, residents);
print(h, r);
}

How to solve the error "expression must be a modifiable lvalue" in c++?

const int ADJ_MATRIX[VERTEX_NUM][VERTEX_NUM]={
{0,1,1,0,0,0,0,0},
{1,0,0,1,1,0,0,0},
{1,0,0,0,0,1,1,0},
{0,1,0,0,0,0,0,1},
{0,1,0,0,0,0,0,1},
{0,0,1,0,0,0,1,0},
{0,0,1,0,0,1,0,0},
{0,0,0,1,1,0,0,0}
};
typedef struct {
int vertex;
int matrix[VERTEX_NUM][VERTEX_NUM];
int vNum;
int eNum;
}Graph;
void buildGraph(Graph *graph){
graph->vNum = VERTEX_NUM;
graph->eNum = EDGE_NUM;
graph->matrix = ADJ_MATRIX;
}
The error occurs in this sentence:
graph->matrix = ADJ_MATRIX;
I am new to c++. please tell me why this problem occur and how to solve it?
I want to assign ADJ_MATRIX to the matrix in struct.
As was said, you can't assign arrays in C++. This is due to the compiler being a meanie, because the compiler can. It just won't let you do it...
... unless you trick it ;)
template <typename T, int N>
struct square_matrix {
T data[N][N];
};
square_matrix<int, 10> a;
square_matrix<int, 10> b;
a = b; // fine, and actually assigns the .data arrays
a.data = b.data; // not allowed, compiler won't let you assign arrays
The catch? Now the code needs some little things:
const square_matrix<int, VERTEX_NUM> ADJ_MATRIX={{
// blah blah
}}; // extra set of braces
typedef struct {
int vertex;
square_matrix<int, VERTEX_NUM> matrix;
int vNum;
int eNum;
}Graph;
void buildGraph(Graph *graph){
graph->vNum = VERTEX_NUM;
graph->eNum = EDGE_NUM;
graph->matrix = ADJ_MATRIX; // no change
}
And to access the cells, now we need to use graph->matrix.data[1][2]. This can be mitigated by overloading operator[] or operator() for square_matrix. However, this is now getting terribly close to the new std::array class, or the Boost equivalent boost::array, so it might be wise to consider those instead.
Unfortunately (or maybe fortunately, who knows...) you can't just assign one array to another in C++.
If you want to copy an array, you will need to either copy each of it's elements into a new array one by one, or use the memcpy() function:
for( int i = 0; i < VERTEX_NUM; i++ )
for( int j = 0; j < VERTEX_NUM; j++ )
graph->matrix[i][j] = ADJ_MATRIX[i][j];
or
memcpy( graph->matrix, ADJ_MATRIX, VERTEX_NUM * VERTEX_NUM * sizeof(int) );
Arrays are not assignable. You can use memcpy:
memcpy(graph->matrix, ADJ_MATRIX, sizeof(graph->matrix));
You cannot assign an array to another array. You will need to copy the elements from the source to the destination index by index, or use memcpy to copy the data. Array assignment like this is not allowed
You are trying to assign your variable address of a constant data,
try using
memcpy(graph->matrix,ADJ_MATRIX,sizeof(ADJ_MATRIX));//using sizeof(graph->matrix) is safer.
You can't use an array in assignments. You may use cycles or memcpy instead
memcpy(graph->matrix, ADJ_MATRIX, VERTEX_NUM * VERTEX_NUM * sizeof(int));
or
for(int i = 0; i < VERTEX_NUM; ++i){
for(int j = 0; j < VERTEX_NUM; ++j){
graph->matrix[i][j] = ADJ_MATRIX[i][j];
}
}
The error is thrown, because int matrix[VERTEX_NUM][VERTEX_NUM] in a structure definition means that each structure will have a 2D array of integers of the predefined size and matrix is going to be pointing to its first element. The thing is that matrix cannot be assigned to an arbitrary address, because it's a const pointer i.e. its value (the address it's pointing to) cannot change.
You have 2 options here: you can either use memcpy or some stl algorithms to copy the ADJ_MATRIX into matrix directly or you can declare matrix as a pointer and do the assignment that is currently produces an error.
The latter can be done in the following way:
typedef struct {
int vertex;
const int (*matrix)[VERTEX_NUM];
int vNum;
int eNum;
}Graph;
Thus you can do graph->matrix = ADJ_MATRIX assignment, but you won't be able to modify the individual items in matrix due to constness. This means, graph->matrix[0][1] = 3; is not allowed, while you can read the elements freely.

Taking integer as parameter and returning an array

I want to create a function that takes an integer as it's parameter and returns an array in C++. This is the pseudo-code I had in mind:
function returnarray(integer i)
{
integer intarr[i];
for (integer j = 0; j < i; j++) { intarr[j] = j; }
return intarr;
}
I tried the common way of declaring returnarray as function* returning a pointer, but then I can't take an integer as my parameter. I also can't assign j to intarr[j]. I'd really like to avoid making a pointer to an int just so I could use the parameter.
Is there any way of doing this and being able to assign j to intarr[j] without making a pointer for it?
EDIT:
forgot to write that I want to avoid vectors. I use them only if I really have to! (my reasons are mine).
Thanks :D
You can't return a stack-allocated array- it's going to go out of scope and the memory deallocated. In addition, C++ does not allow stack-allocated variable-length arrays. You should use a std::vector.
std::vector<int> returnarray(int i) {
std::vector<int> ret(i);
for(int j = 0; j < i; j++) ret[j] = j;
return ret;
}
your code isn't even near valid c++ so i assume you're total beginner
use std::vector
#include <vector>
std::vector<int> yourFunction( int n )
{
std::vector<int> result;
for( int i = 0; i < n; ++i )
{
result.push_back( i );
}
return result;
}
Disclaimer: code untouched by compilers' hands.
Cheers & hth.,
Two remarks, before using the excellent #DeadMG 's solution:
1) You never want to avoid vectors. If v is a vector, and you really want a pointer, you can always have a pointer to the first element by writing &v[0].
2) You can't return an array. You will return a pointer to a fresh memory zone that you'll have to delete once finished with it. Vectors are only arrays with an automatic deletion facility, so you won't leak memory.
You need to use dynamic memory. Something like this
int* returnArray(int size) {
int* array = new int[size];
for(int i = 0; i < size; ++i)
array[i] = i;
return array;
}
Not that I'd particularly recommend this approach in general, but you can use templates to do this without resorting to dynamic memory allocation. Unfortunately you can't return arrays from functions, so you'd need to return a struct with the array inside.
template <int N>
struct int_array_type {
int ints[N];
};
template <int N>
int_array_type<N> returnarray() {
int_array_type<N> a;
for (int i = 0; i < N; ++i)
a.ints[i] = i;
return a;
}
...
int_array_type<10> u = returnarray<10>();
std::copy(u.ints, u.ints+sizeof(u.ints)/sizeof(u.ints[0]),
std::ostream_iterator<int>(std::cout, "\n"));