I have an class which I wish to instantiate by passing an array of values. The object here has two members but this has been reduced for illustration. In the future I will read values from disk and then create an object from those values, hence the array. The object will have multiple pointers later on hence the shared_ptr.
Firstly, I would like to know if this would prevent memory leaks as is. Secondly, I would like to know if there is are less bloated ways of instantiating objects and then determinsitically destroying them later on.
Class Header file:
//MyClass.hpp
#pragma once
#include "stdafx.h"
#include <array>
class SimpleClass{
private:
//Per object
double var1;
double var2;
public static:
//For calling when using templates such as std::array<>
//Using __int8 as will not be > 256
static const uint8_t varCount 2;
SimpleBody(std::array<double, varCount> inputArray);
~SimpleBody();
}
Class Implementation
//MyClass.cpp
#pragma once
#include "stdafx.h"
#include <array>
#include "body.hpp"
SimpleBody::SimpleBody(std::array<double, SimpleBody::varCount> inputArray ) {
//Assign var1
MyClass::var1= inputArray[0];
//Assign var2
MyClass::var2= inputArray[1];
};
SimpleBody::~SimpleBody() {
//Add in code here when children need deleting
};
Entry Point
// EntryPoint.cpp
//
#include "stdafx.h"
#include "MyClass.hpp"
#include <array>
#include <memory>
int main()
{
//Create an array with a smart pointer for memory management
std::unique_ptr< std::array<double, MyClass::varCount> > initArray =
std::make_unique< std::array<double, MyClass::varCount> >();
//Define values
*initArray = { 1.0,2.0 };
//Use array to create object
std::shared_ptr<MyClass> object = std::make_shared<MyClass>(*initArray );
//Free memory
initArray.reset();
object.reset();
return 0;
}
In your example you could skip the unique pointer because you only want the values anyway
int main()
{
std::array<double, MyClass::varCount> initArray = { 1.0,2.0 };
std::shared_ptr<MyClass> object = std::make_shared<MyClass>(initArray );
return 0;
}
This would do the same.
For "less bloated":
There is always auto:
int main()
{
auto initArray = std::make_unique< std::array<double, MyClass::varCount> >();
*initArray = {1.0,2.0};
auto object = std::make_shared<MyClass>(*initArray );
return 0;
}
First of all its bad if you pass the array by value in the SimpleBody constructor. Better use a reference
SimpleBody(const std::array<int, varCount> &inputArray);
At the moment, you construct your shared_ptr the unique_ptr looses the ownership of the array. You don't need to do reset manually. Also, at the moment, your program hits the } line, the memory will be freed.
Related
In C styled linked list you just set pointer to allocated object, while in C++ copy seems unavoidable.
Naive test code:
#include <cstring>
#include <iostream>
#include <chrono>
#include <memory>
#include <list>
#include <vector>
using std::cout;
struct LinkedS{
LinkedS *next = nullptr;
float f;
std::vector<float> v{1};
};
struct S{
float f;
std::vector<float> v{1};
};
int main()
{
S* s = new S;
cout << &s->v.front() << ' ' << &s->f << '\n';
std::list<S> li;
li.push_front( std::move( *s ) );
cout << &li.front().v.front() << ' ' << &li.front().f << '\n';
}
https://godbolt.org/z/33T4179Gj
Here vector content is actually moved, but struct data is still copied unfortunately.
This usage of new is not a good C++, it is not Java/C#, do not use new unless you have to and when you have to, use std::unique_ptr.
Are you looking for emplace_front, emplace_back? They can construct the stored object at its final destination.
C++20
#include <list>
#include <vector>
struct S{
float f;
std::vector<float> v;
};
int main()
{
std::list<S> li;
li.emplace_front(42.4f,std::vector{1.f,2.f,3.f,4.f,5.f});
}
C++17
Sadly prior to C++20, a constructor is required, aggregates do not count.
#include <list>
#include <vector>
struct S{
float f;
std::vector<float> v;
S(float f, std::vector<float> v):f(f),v(std::move(v)){}
};
int main()
{
std::list<S> li;
li.emplace_front(42.4f,std::vector{1.f,2.f,3.f,4.f,5.f});
}
If you really need to pass a pre-allocated pointer to a container, it is not possible. std::list is not a linked list per se, it only has requirements on operations that will likely lead to linked-list implementation. Furthermore all STL containers use customizable Allocators and each allocator owns and provides the storage for its container, it cannot be passed in from an external source.
Although there are some recent exceptions for associative containers with std::map::extract and std::map::insert operating on nodes. In theory, std::list might get them too in the future.
True linked list
Nothing is stopping you from creating std::list<std::unique_ptr<T>> and working with that.
Following codeblock is compiling and running okay.
Qeus-1. Is it safe to memset a struct which contains another stuct with smart pointer as a member variable? (like the example code below)
Ques-2. Is it safe to memset a struct which contains smart pointer members?
Following code structure is part of a legacy project where this hierarchical structures have hundreds of other members (POD or non POD memebers)
#include <iostream>
#include <map>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <memory>
typedef struct _Globals{
std::shared_ptr<std::map<int, std::string> > rollNamePair;
} _Globals;
struct _Class {
struct _Globals Globals; // global vars
};
struct _School {
struct _Class *pSchool;
};
int main()
{
struct _School abc;
memset(&abc, 0, sizeof(struct _School));
abc.pSchool= (struct _Class*) malloc(sizeof(struct _Class));
abc.pSchool->Globals.rollNamePair= std::make_shared<std::map<int, std::string> >();
(*abc.pSchool->Globals.rollNamePair)[1]= "John";
(*abc.pSchool->Globals.rollNamePair)[2]= "Paul";
std::cout << (*abc.pSchool->Globals.rollNamePair)[1] << "\n";
std::cout << (*abc.pSchool->Globals.rollNamePair)[2];
return 0;
}
No, never use memset on any structure which is not POD.
However your code isn't doing that, it is only calling memset on _School which is POD as it only contains a pointer, calling memset on _Class or _Globals would have undefined behaviour. However I'd prefer removing the memset and adding a constructor to _School which initialises pSchool to nullptr:
struct _School {
_Class *pSchool;
_School() : pSchool(nullptr) {}
};
You need to use new in C++ code rather than malloc as malloc doesn't call class constructors.
Also note that identifiers starting with underscore followed by an uppercase character are reserved for use by the compiler/standard library.
The complete code would be:
#include <map>
#include <string>
#include <memory>
#include <iostream>
struct Globals{
std::shared_ptr<std::map<int, std::string> > rollNamePair;
};
struct Class {
Globals Globals; // global vars
};
struct School {
Class *pSchool;
School() :pSchool(nullptr) {}
};
int main()
{
School abc;
abc.pSchool= new Class();
abc.pSchool->Globals.rollNamePair = std::make_shared<std::map<int, std::string> >();
(*abc.pSchool->Globals.rollNamePair)[1] = "John";
(*abc.pSchool->Globals.rollNamePair)[2] = "Paul";
std::cout << (*abc.pSchool->Globals.rollNamePair)[1] << "\n";
std::cout << (*abc.pSchool->Globals.rollNamePair)[2];
delete abc.pSchool;
return 0;
}
To address the second question, if you have a struct
struct G {
std::shared_ptr<T> ptr;
};
that contains a smart pointer as its member, then doing
G g;
std::memset(&g, 0, sizeof(G));
is definitely not safe, because you overwrite g.ptr object of a non-POD type that has already been constructed.
What you could do is something like this:
std::aligned_storage_t<sizeof(G), alignof(G)> storage; // Raw storage of some POD type
std::memset(&storage, 0, sizeof(G));
auto g = new (&storage) G;
g->ptr = std::make_shared ... ;
// ...
std::destroy_at(g);
There is no reason to use memset in this particular example, but it is legal and safe.
I need to copy array of vector passed as reference to constructor/function.
member variable gets the reference to the passed array of vector
while running below code I am getting error Expression: Transposed pointer range
#define MAX 1001
Constructor( std::vector< int > (&Adj)[MAX])
{
(this->Adj[MAX]) = Adj[MAX];
}
If you really want an array of fixed (compile-time) size of std::vector, use std::array
#include <array>
#include <vector>
Constructor (const std::array<std::vector<int>, MAX>& rhs)
{
this->Adj = rhs;
}
and declare Adj as
std::array<std::vector<int>, MAX> Adj
You can use std::copy to copy an array:
#include <cstddef> // size_t
#include <vector> // std::vector<>
#include <algorithm> // std::copy();
constexpr size_t max{ 3 };
struct foo {
std::vector<int> Adj[max];
foo(std::vector<int> (&values)[max])
{
std::copy(std::begin(values), std::end(values), std::begin(Adj));
}
};
int main()
{
std::vector<int> values[max];
foo f{ values };
}
struct thread_data
{
int seq, packetNum;
bool ackTally[4];
};
main:
bool boolArray[4];
data[j].packetNum = 3;
data[j].seq = 1;
data[j].ackTally = boolArray;
Running this code gives me this error
error: incompatible types in assignment of bool [1] to bool [0]
data[j].ackTally = boolArray;
How do I resolve this in code? (It may not be relevant by I'll probably want to change some of these values later within the main code)
C-style arrays cannot be assigned. There are two simple ways around this:
First, use std::copy instead of assignment:
#include <algorithm>
#include <iterator>
struct thread_data
{
int seq, packetNum;
bool ackTally[4];
};
int main() {
thread_data data;
bool boolArray[4];
// Initialize boolArray.
std::copy( std::begin(boolArray), std::end(boolArray),
std::begin(data.ackTally) );
}
Or second, replace your C-style arrays with std::array. One of several benefits of std::array is that it can be assigned:
#include <array>
struct thread_data
{
int seq, packetNum;
std::array<bool, 4> ackTally;
};
int main() {
thread_data data;
std::array<bool, 4> boolArray;
// Initialize boolArray.
thread_data.ackTally = boolArray;
}
Arrays can not be assigned. Each element must be copied in a loop. You don't have to write that loop yourself though, since there is an algorithm for that in the standard library: std::copy.
P.S. Copying from boolArray will have undefined behaviour because its values are indeterminate.
I have structs templated by int derived from a Base struct.
struct Base { int i; double d; };
template< int N > struct Derv : base { static const int mN = N; };
I need to make an array of Derv< N > where N can vary for each struct in that array. I know C/C++ does not allow arrays of objects of different types, but is there a way around this? I was thinking of separating the type information somehow (hints like pointers to Base struct or usage of union spring to my mind, but with all of these I don't know how to store the type information of each array element for usage DURING COMPILE TIME). As you can see, the memory pattern of each Derv< N > is the same.
I need to access the type of each array element for template specialization later in my code. The general aim of this all is to have a compile-time dispatch mechanism without the need to do a runtime "type switch" somewhere in the code.
It is most certainly impossible. If you did
int i;
std::cin >> i;
some_magic_array X[size];
Then what is the type of X[i]? Oh, wait, you can't possibly know. It's nothing C++ specific, it's fundamentally impossible. That's why no some_magic_array will ever exist that permits this.
Unless you effectively use a std::tuple and guarantee that i is constexpr. Then you absolutely can do this with std::get<i>(tup);.
I guess you can use ptr = dynamic_cast<Type>(element); .. ptr will equal to NULL if it's the wrong type.
For example:
#include <map>
#include <cmath>
#include <vector>
#include <string>
#include <cstdlib>
#include <fstream>
#include <sstream>
#include <iostream>
using namespace std;
struct Base { int i; double d; Base(){}; virtual ~Base(){};};
template< int N > struct Derv : public Base { static const int mN = N; ~Derv(){}; };
int main(int argc, char **argv){
Base* arr[2];
arr[0] = new Derv<10>;
arr[1] = new Derv<5>;
Derv<10> *ptr = dynamic_cast<Derv<10>* >(arr[0]);
Derv<5> *ptr2 = dynamic_cast<Derv<5>* >(arr[0]);
cout << ptr << endl << ptr2 << endl;
return 0;
}
// That should print
0x9571008 //ptr to object will differ every time.
0 // Null because of wrong type.
But you'll need to define virtual destructor in your struct for this to work, and/or a virtual function.