Class array initialization - c++

Good morning.
Question is about initialisation of object.
Its easy to do like this:
class MyClass {
char* data;
public:
inline MyClass(char* s) {
data = s;
};
}
MyClass Obj = "foo";
But is it possible to make initialization syntax(with the same actions) like this?
MyClass Obj[] = "foo";
I need brackets for compatibility with other compiler :/

Have you tried using the initialization list using {} ?
MyClass Obj[] = {"foo"};
or
MyClass Obj[] = {"foo", "bar"};

Related

Builder pattern with struct

I'm trying to implement a builder patter in C++. That what i got by now:
struct Person {
std::string name;
uint32_t age;
std::vector<std::string> pet_names;
};
class PersonBuilder {
public:
PersonBuilder& SetName(std::string name) {
person_.name = std::move(name);
return *this;
}
PersonBuilder& SetAge(uint32_t age) {
person_.age = age;
return *this;
}
PersonBuilder& SetPetNames(std::vector<std::string> pet_names) {
person_.pet_names = std::move(pet_names);
return *this;
}
Person Build() {
return Person(std::move(person_));
}
private:
Person person_;
};
int main() {
auto person = PersonBuilder()
.SetName("John")
.SetAge(23)
.SetPetNames({"rax", "rbx", "rcx"})
.Build();
return EXIT_SUCCESS;
}
I'm a little worried about unitilized uint32, and std::move(person_) inside a Build method. Am i doing it right or there is a better way, with a struct constructor for example?
I'm a little worried about unitilized uint32
You initialise it before you use it, so there is no problem in the example. It is however easy for a user of the builder to forget calling one of the setters leaving it with indeterminate value resulting in undefined behaviour. You give it a default member initialiser to avoid that.
and std::move(person_) inside a Build method.
It's unnecessary to repeat the name of the class there. This would also work:
return std::move(person_);
It's also important for the caller to understand that Build may only be called once per initialisation. If the builder is intended to only be used as a temporary like in the example, you could add rvalue reference qualifier to the Build function to prevent some of the accidental misuse.
or there is a better way,
I don't see the benefit of the builder. Why not just use aggregate initialisation?:
Person person {
.name = "John",
.age = 23,
.pet_names = {"rax", "rbx", "rcx"},
};
You did it right.
You can also turn the set function into a template function, which can avoid the modification of PersonBuilder when adding new member variables for Person.
struct Person {
std::string name;
uint32_t age;
std::vector<std::string> pet_names;
};
class PersonBuilder {
public:
template<auto mem_ptr>
PersonBuilder& Set(std::remove_reference_t<std::invoke_result_t<decltype(mem_ptr), Person&>> value = {}) {
std::invoke(mem_ptr, person_) = std::move(value);
return *this;
}
Person Build() {
auto result = std::move(person_);
person_ = Person();
return result;
}
private:
Person person_;
};
int main() {
auto person = PersonBuilder()
.Set<&Person::name>("John")
.Set<&Person::age>(23)
.Set<&Person::pet_names>({"rax", "rbx", "rcx"})
.Build();
}
Demo.

Do I need to initialize std::string

I have this code:
class myclass
{
std::string str;
public:
void setStr(std::string value)
{
str=value;
}
std::string getStr()
{
return str;
}
}
main()
{
myclass ms;
std::cout<<ms.getStr()<<std::endl;
}
when I compile and run this code, there is o error and in windows I am always getting str as "".
Is this always valid?
I need the above behavior in the fact that if user did not call set, str would be always a blank string.
should I initialize str in constructor as follow:
class myclass
{
std::string str;
public:
myclass():str(""){}
void setStr(std::string value)
{
str=value;
}
std::string getStr()
{
return str;
}
}
I want to make sure that behavior is the same on all platform and also make sure that code is as small and neat as possible.
Do I need to initialize std::string
No. std::string default constructor initialises a nice empty string for you.
I want to make sure that behavior is the same on all platform and also make sure that code is as small and neat as possible.
Remove clutter then:
struct myclass {
std::string str;
};
Fundamental types though, do not get initialised by default, you need to initialize them explicitly:
struct myclass {
std::string str;
int i = 1; // <--- initialize to 1.
};
You dont need to initialize a string member with an empty string, though it can help to do it anyhows. Consider:
struct foo {
std::string a;
std::string b;
foo() : a("foo") {}
};
Was it by accident or on purpose that b does not get a value in the constructor? I'd prefer
foo() : a("foo"), b() {}
because it makes the intend explicit for no price (not counting a few keystrokes).

initializing const fields in the constructor

const fields in C++ must be initialized in the initialization list, this makes non trivial the computation of interdependent values from the constructor parameters.
What is(are) the best way(s) to translate, for example, this piece of java code into c++ ?
public class SomeObject {
private final String some_string;
private final int some_int;
public SomeObject(final String input_filename){
SomeReader reader(input_filename);
some_string = reader.getString();
some_int = reader.getInt();
reader.close();
}
}
I thought of encapsulating a sub-object in SomeObject, but this is just shifting the problem; or constructing the object using a static method:
class SomeObject {
private:
const std::string some_string;
const int some_int;
public:
static SomeObject unserialize(const char * input_filename){
SomeReader reader = new SomeReader(input_filename);
string str = reader.get_string();
int i = reader.get_int();
reader.close();
SomeObject obj(str, i);
return obj;
};
SomeObject(const std::string str, const int i) :
some_string(str),
some_int(i)
{};
}
Is there a better solution ?
Thank you.
This is a great application for C++11 constructor delegation:
class SomeObject {
private:
const std::string some_string;
const int some_int;
public:
// The "real" constructor for SomeObject
SomeObject(std::string str, const int i) :
some_string{std::move(str)},
some_int{i}
{}
// Deserialize from SomeReader (delegates to the primary constructor)
SomeObject(SomeReader& reader) :
SomeObject{reader.get_string(), reader.get_int()} {}
// Deserialize from SomeReader (accepts rvalues,
// delegates to the lvalue constructor)
SomeObject(SomeReader&& reader) :
SomeObject{reader} {}
// Deserialize from named file (delegates to the SomeReader&& constructor)
SomeObject(const char* input_filename) :
SomeObject{SomeReader{input_filename}} {}
};
You can use a delegating ctor and a lambda-function, like this:
SomeObject(const char* filename) : SomeObject([&]{
/* Do calculations here */
return std::make_tuple(...);
}()) {}
SomeObject(std::tuple<...>&& x) : /* ... */ {}
Still, a much better idea is probably a re-design to make use of all the things you can do in C++ and cannot do in Java.
I think you have the right approach.
I would recommend couple of minor changes.
This is not correct C++.
SomeReader reader = new SomeReader(input_filename);
Perhaps you meant:
SomeReader reader(input_filename);
You can change the lines:
SomeObject obj(str, i);
return obj;
to
return SomeObject(str, i);

automatic conversion from string to myclass

I have defined a class
class Version
{
public:
Version(std::string versionStr)
{
//do something
}
}
I want to be able to use it as follow
void foo(Version v1) {//do somthing};
void main()
{
foo("test");
}
I would like that v1 becomes an object as if I have done:
void main()
{
Version v1("test");
foo(v1);
}
is that possible?
The code you have has too many levels of implicit construction. "string literal" is of type const char [] and not std::string. Only one level of implicit construction occurs automatically. Try adding a constructor that takes const char * instead:
class Version {
// ...
Version(const char *_vstr) : versionStr(_vstr) {}
// ...
}
Live demo.

Initialize struct in class constructor

How can we initailize a struct pointer in constructor of a class.
Example:
struct my_struct{
int i;
char* name;
};
class my_class{
my_struct* s1;
my_class() {
// here i want to make s1->i = 10; and s1->name = "anyname" ;
// should i assign it like s1->i= 10; and call new for s1->name and strcpy(s1->name "anyname");
// it compiles in g++ without any warning/error but gives seg fault at run time
}
};
I'm surprised that no-one has suggested the following...
struct my_struct
{
int i;
std::string name;
my_struct(int argI, std::string const& argName) : i(argI), name(argName) {}
};
class my_class
{
my_struct s1; // no need for pointers!
my_class() : s1(1, std::string("test name")) {} // construct s1 using the two argument constructor, can also default construct as well.
};
With this approach, you don't need to worry about cleaning up s1, it's automatic...
When you create an instance of my_class, the s1 pointer doesn't point to anything. You have to allocate memory for it like so:
myclass() {
s1 = new my_struct;
// initialize variables
}
You will also have to create a destructor for it:
~myclass() {
// delete variables
delete s1;
}
Also, since this is C++, I recommend you use std::string instead of char*s.
Since this is C++, use std::string instead of char*:
struct my_struct{
int i;
std::string name;
};
class my_class{
my_struct* s1;
my_class() {
s1 = new my_struct;
s1->i = 10;
s1->name = "anyname";
}
};
The reason your original code segfaulted was that you failed to allocate memory for s1 and also failed to allocate memory for s1->name. I've fixed the former with the new and the latter by using std::string. If for some reason you can't use std::string, use strdup where you were trying to use strcpy.
Lastly, don't forget to provide a destructor for my_class that'll delete s1 (and will free s1->name if you opt for char* and strdup).
I'm pretty sure you can use an initialization list, and new+init the struct directly. Also, you can't forget that you have to delete the pointer when you're done:
struct my_struct{
int i;
char* name;
};
class my_class{
my_struct* s1;
my_class() : s1(new my_struct) {
s1->i = 2;
s1->name = "Something";
}
~my_class() { delete s1; }
};
Also, make sure you're using a char* for a reason, otherwise a std::string will most often be better.
If the structure is inside the class, you can use the structure constructor:
struct my_struct
{
int i;
std::string name;
my_struct()
{
i = 10;
name = "anyname";
};
};
If it's global, you first need to create the object and then initialize it:
class my_class
{
my_struct * s1;
my_class() : s1(new my_struct())
{
s1->i = 10;
s1->name = "anyname";
}
};
my_class() {
s1 = new (my_struct);
s1->i = 10;
s1->name = (char *) malloc(strlen("anyname"));
s1->name = "anyname";
// here i want to make s1->i = 10; and s1->name = "anyname" ;
// should i assign it like s1->i= 10; and call new for s1->name and strcpy(s1->name "anyname");
// it compiles in g++ without any warning/error but gives seg fault at run time
}
~my_class(){
free(s1->name);
delete s1;
}