Setting char* properties of a class - c++

I have 10 char* properties of my class called Car,what is the best way to write the setters of these 10 char* properties? One way is to directly set the value in it :
void Car<T>::setKey(const char* toCopyKey)
{
delete[] key;
int length=strlen(toCopyKey);
key=new char[length+1];
strcpy(key,toCopyKey);
}
and do this 10 times ,other solution I thought of , is to make a function that creates a copy of the passed char* and then assigns it in the setter :
char* Car<T>::copyString(const char* s)
{
int length=strlen(s);
char* property=new char[length+1];
strcpy(property,s);
return property;
}
and use the copyString method in every setter like this :
void Car<T>::setModel(const char* toCopyModel)
{
delete[] model;
model=copyString(toCopyModel);
}
But I was wondering if this second solution is correct and if there is a better way to do this copying?I cannot use std::string and vector.

I guess this is an assignment of some C++ course or tutorial, because otherwise I would recommend to question the whole design.
In general, I would learn as early as possible to not do manual memory management at all and use C++ standard library smart pointers. This relieves you from the burden to write destructors, copy|move-assignment and copy|move constructors.
In your example, you could use std::unique_ptr<char[]> to hold the string data. This is also exception safe and prevents memory leaks. Creation of the unique_ptr<char[]> objects can be centralized in a helper method.
class Car {
private:
std::unique_ptr<char[]> model;
std::unique_ptr<char[]> key;
static std::unique_ptr<char[]> copyString(char const* prop) {
auto const len = std::strlen(prop);
auto p = std::make_unique<char[]>(len+1);
std::copy(prop, prop + len, p.get());
p[len] = '\0';
return p;
}
public:
void setModel(char const* newModel) {
model = copyString(newModel);
}
void setKey(char const* k) {
key = copyString(k);
}
char const* getModel() const {
return model.get();
}
};
If you don't know them, I would recommend to read about the rule of zero.

You can combine your two methods by using a reference parameter:
static void Car<T>::setStringProp(char *&prop, const char *toCopyString) {
delete[] prop;
prop = new char[strlen(toCopyString)+1];
strcpy(prop, toCopyString);
}
void Car<T>::setModel(const char *toCopyModel) {
setStringProp(model, toCopyModel);
}
And make sure that your constructor initializes all the properties to NULL before calling the setters, because delete[] prop requires that it be an initialized pointer.
Car<T>::Car<T>(const char *model, const char *key, ...): model(nullptr), key(nullptr), ... {
setModel(model);
setKey(key);
...
}

Related

From (pointer to a list of pointer to class) to list of classes

I have this function where I need to return const std::list<Album> , and what I have is std::list<Album*>*.
How can I convert the first type to the second type?
const std::list<Album> DatabaseAccess::getAlbums()
{
char* sqlStatement = "SELECT * FROM Albums;";
char* errMessage = nullptr;
std::list<Album*>* data = new std::list<Album*>;
sqlite3_exec(this->_DataBase, sqlStatement, Album::callback, data, &errMessage);
return ; // ?
}
EDIT:
Will this work?
std::list<Album> casted_list;
for (auto i = data->begin(); i != data->end(); i++)
{
casted_list.push_back(*(*i));
}
It looks like you are using a lot of unnecessary pointers and dynamic allocation in your code. It is hard to be sure without seeing what your Album objects look like but I would probably do something like this:
class Album
{
public:
Album(std::string const& name): m_name(name) {}
static int callback(void* uptr, int no_of_cols, char** results, char** column_names)
{
// cast our void* to what we are working with
std::list<Album>& albums = *reinterpret_cast<std::list<Album>*>(uptr);
// pass the Album's constructor arguments to emplace(...)
albums.emplace_back(results[0]);
return {};
}
private:
std::string m_name;
};
class DatabaseAccess
{
public:
std::list<Album> getAlbums();
private:
sqlite3* _DataBase = nullptr;
};
std::list<Album> DatabaseAccess::getAlbums()
{
char const* sqlStatement = "SELECT * FROM Albums;";
char* errMessage = nullptr;
// No need to get into pointers here
std::list<Album> data;
// send the address of data to the callback function
sqlite3_exec(this->_DataBase, sqlStatement, Album::callback, &data, &errMessage);
return data; // then just return your list
}
As far as I can tell you don't really need to create anything other than a std::list<Album> (your desired return type) to begin with and avoid all the pointers.

How to create pool of contiguous memory of non-POD type?

I have a scenario where I have multiple operations represented in the following way:
struct Op {
virtual void Run() = 0;
};
struct FooOp : public Op {
const std::vector<char> v;
const std::string s;
FooOp(const std::vector<char> &v, const std::string &s) : v(v), s(s) {}
void Run() { std::cout << "FooOp::Run" << '\n'; }
};
// (...)
My application works in several passes. In each pass, I want to create many of these operations and at the end of the pass I can discard them all at the same time. So I would like to preallocate some chunk of memory for these operations and allocate new operations from this memory. I came up with the following code:
class FooPool {
public:
FooPool(int size) {
foo_pool = new char[size * sizeof(FooOp)]; // what about FooOp alignment?
cur = 0;
}
~FooPool() { delete foo_pool; }
FooOp *New(const std::vector<char> &v, const std::string &s) {
return new (reinterpret_cast<FooOp*>(foo_pool) + cur) FooOp(v,s);
}
void Release() {
for (int i = 0; i < cur; ++i) {
(reinterpret_cast<FooOp*>(foo_pool)+i)->~FooOp();
}
cur = 0;
}
private:
char *foo_pool;
int cur;
};
This seems to work, but I'm pretty sure I need to take care somehow of the alignment of FooOp. Moreover, I'm not even sure this approach is viable since the operations are not PODs.
Is my approach flawed? (most likely)
What's a better way of doing this?
Is there a way to reclaim the existing memory using unique_ptrs?
Thanks!
I think this code will have similar performance characteristics without requiring you to mess around with placement new and aligned storage:
class FooPool {
public:
FooPool(int size) {
pool.reserve(size);
}
FooOp* New(const std::vector<char>& v, const std::string& s) {
pool.emplace_back(v, s); // in c++17: return pool.emplace_back etc. etc.
return &pool.back();
}
void Release() {
pool.clear();
}
private:
std::vector<FooOp> pool;
}
The key idea here being that your FooPool is essentially doing what std::vector does.

Why initializing a second object does modify my first object?

I have a class IStream2:
class IStream2 {
private:
char* fn;
public:
IStream2(char* filename);
char* get_filename();
};
IStream2::IStream2(char *filename) {
strcpy(fn, filename);
}
char * IStream2::get_filename() {
return fn;
}
And here is the main code:
vector<IStream2> istreams;
char fn[] = "file1.txt";
IStream2 reader2 = IStream2(fn);
istreams.push_back(reader2);
char fn2[] = "file2.txt";
IStream2 reader3 = IStream2(fn2);
istreams.push_back(reader3);
cout << istreams[0].get_filename() << endl;
It prints file2.txt but I expected file1.txt.
I know that I should use string but I would like to resolve this problem.
IStream2::IStream2(char *filename) {
strcpy(fn, filename);
}
Allocates no storage for fn. strcpy(fn, filename); invokes undefined behaviour writing into whatever storage fn points at, and after that all bets are off. The program could do anything.
The right answer is to use std::string
class IStream2 {
private:
std::string fn;
public:
IStream2(const char* filename); // note const. if not modifying a passed rference,
// mark it const. The compiler can make optimizations
// and can catch mistakes for you
// also the function can now receive string literals
const char* get_filename(); // another const this is because a string won't
// easily give you a non const pointer
}; <-- note the added ;
IStream2::IStream2(const char *filename): fn(filename) {
}
const char * IStream2::get_filename() {
return fn.c_str(); // get the char array from the string
}
But I suspect this is an exercise in writing C with Classes, so back into the stone ages we go. This is a LOT more work because we have to manage all of the memory ourselves. For example, We need to observe the Rule of Three. What is The Rule of Three?
Sigh.
class IStream2 {
private:
char* fn;
public:
IStream2(const char* filename); // note const char *
~IStream2(); // need destructor to clean up fn. This means we need to
// comply with the Rule of Three
IStream2(const IStream2 & src); // copy constructor
IStream2 & operator=(IStream2 src); // assignment operator
char* get_filename(); // Note: by returning a non const pointer here we
// allow the caller to tamper with the contents of
// fn and even delete it. This defeats the point
// of declaring fn private, so avoid doing this.
};
IStream2::IStream2(const char *filename) {
fn = new char[strlen(filename) +1]; // allocate storage.
// The +1 is to hold the string's NULL terminator
strcpy(fn, filename);
}
// implement copy constructor
IStream2::IStream2(const IStream2 & src) {
fn = new char[strlen(src.fn) +1];
strcpy(fn, src.fn);
}
// implement destructor
IStream2::~IStream2()
{
delete[] fn;
}
// implement assignment operator. Using Copy And Swap Idiom
IStream2 & IStream2::operator=(IStream2 src)
{
std::swap(fn, src.fn);
return *this;
}
char * IStream2::get_filename() {
return fn;
}
int main()
{
vector<IStream2> istreams;
const char* fn = "file1.txt"; // note const char *. string literals may be
// read-only memory and can't be changed
IStream2 reader2 = IStream2(fn);
istreams.push_back(reader2);
const char* fn2 = "file2.txt"; // and again const char *
IStream2 reader3 = IStream2(fn2);
istreams.push_back(reader3);
cout << istreams[0].get_filename() << endl;
return 0;
}
Since we are wresting with dinosaurs, I won't bother with the Rule of Five and move operations, but see how much more annoying it is to have to do this the wrong way?
More on Copy And Swap Idiom

error: Cannot access memory at address

I'm trying to return pointer from function in derived class,
This is the code:
class A
class A {
protected:
C c;
public:
virtual void func(){
unsigned char *data;
int size=getData(data);
}
}
class B
class B : public A {
private:
int size;
public:
B(const C &_c) { c=_c; };
const int B::getData(unsigned char *data) const {
data=(unsigned char *)malloc(size);
memcpy(data,c.getMem(),size);
if(!data){
//err
}
return size;
}
class C
class C {
private:
unsigned char *mem;
public:
C(unsigned char *_mem) : mem(_mem);
const unsigned char *getMem() const { return mem; };
}
main.cpp
C c(...);
B *b=new B(c);
b->func();
The error I get when getData returns
(this=0x50cdf8, data=0x2 <error: Cannot access memory at address 0x2>, size=32)
Thanks.
In class A, the func() is worthless because:
1. size is not returned to the caller.
2. The pointer data is local to func and it's contents will disappear after the end of execution in func().
You don't need to return const int from a function. A function will return a copy of variables, so they are constant (copies).
Don't use malloc in C++, use operator new. The malloc function does not call constructors for objects.
Don't use new or malloc unless absolutely necessary. Usually, dynamic memory is for containers where their capacity is not known at compile time; or for objects that live beyond a function or statement block's execution time. If you must use dynamic memory, use a smart pointer (like boost::shared_ptr).
Use std::vector for dynamic arrays. It works; it's been tested; you don't have to write your own, including memory management.
You are passing data by value (a copy of the pointer). If you want to modify a pointer, pass it by reference or pass a pointer to the pointer; (what I call the address of the pointer).
Example:
void my_function(uint8_t * & p_data)
{
p_data = new uint8_t[512];
}
Or
void another_function(unsigned char * * pp_data)
{
*pp_data = new unsigned char [1024];
}
A much better solution:
void better_function(std::vector<uint8_t>& data)
{
data.reserve(64);
for (uint8_t i = 0; i < 64; ++i)
{
data[i] = i;
}
}

C++ - A question about memory management

I have the following class:
class StringHolder
{
public:
StringHolder(std::string& str)
{
m_str = &str;
}
private:
std::string* m_str;
};
And have the following string object (str), with a size of 1,024KB:
char c = 'a';
unsigned long long limit = 1024 * 1024;
std::stringstream stream;
for(int i = 0; i < limit; i++)
{
stream << c;
}
std::string str = stream.str();
Whenever I initialize the StringHolder class with a string, it doesn't make a copy of that string. That's because I used references & pointers, but I'm not sure if I used them properly :\
The question: did I use references & pointers properly?
The correct implementation should be something like
class StringHolder
{
public:
StringHolder(const std::string& str) //note const
: m_str(str){} //use initialization list
private:
std::string m_str; //no need to make it a pointer
};
Under the assumption that the lifetime of the string is known to outlive the object that will refer to it, and that the object will not need to modify the string, then you can take a constant reference to the string in the constructor and store that in:
class StringHolder {
std::string const & str;
public:
StringHolder( std::string const & s ) : str(s) {}
};
With this solution the contents of the string will not be copied, but any change to the original string will affect the contents seen from the reference. Also, this does not allow you to change the string referred to after construction. If you need to reseat the reference you will have to do with a pointer:
class StringHolder {
std::string const * str;
public:
StringHolder( std::string const & s ) : str(&s) {}
void reset( std::string const & s ) {
str = &s;
}
};
The fact that you need to store a pointer instead of a reference is an implementation detail, so you can (as I did) hide it from the user, or you could change the interface to take the string by pointer to make it more explicit (I prefer the former, but the later has the effect of self documentation: it is clearer that lifetimes have to be considered).
Also note that with the implementation as shown, a temporary can be used to initialize/set the string, causing undefined behavior: temporary dies, reference points to nowhere.