Proper Class Modeling using C++ and const - c++

Let me preference that I mostly develop in C# and the C++ development which I have done did not fully leverage the C++ language. I am now trying to use the language as it was intended and I am pulling my hair out with const declarations in passed arguments. In the past I never used them or hacked my way into making them work with the STL.
My understanding that I would create the following function when I want to use o as readonly in the function:
void foo(const MyClass* o);
So here is my problem...code first:
#include <iostream>
#include <string>
using namespace std;
///////////////////////////////////////////////////////////
// Classes are defined in the one file for an easy post.
///////////////////////////////////////////////////////////
class ClassA {
private: // member variables
string m_name;
public: // constructors
ClassA(const string& name = "") : m_name{name} {}
virtual ~ClassA() { }
public: // accessors
const string& name() const { return m_name; }
void setName(const string& value) { m_name = value; }
};
class ClassB {
private: // member variables
string m_name;
ClassA m_child;
public: // constructors
ClassB(const string& name = "") : m_name{name} {}
virtual ~ClassB() { }
public: // accessors
const string& name() const { return m_name; }
void setName(const string& value) { m_name = value; }
ClassA* child() { return &m_child; }
void setChild(const ClassA* value) { m_child = *value; }
};
///////////////////////////////////////////////////////////
// Protoptypes are not used to save space for the post.
void doSomethingA(const ClassA* o) {
cout << "name = " << o->name() << endl << endl;
}
void doSomethingB(const ClassB* o) {
cout << "name = " << o->name() << endl << endl;
doSomethingA(o->child());
}
///////////////////////////////////////////////////////////
int main(int argc, char** argv) {
ClassA a { "My Class A" };
ClassB b { "My Class B" };
b.setChild(&a);
b.child()->setName("My New Name");
doSomethingB(&b);
return 0;
}
In main() the compiler (g++ version 4.7.2) balks in doSomethingB:
doSomethingA(o->child());
with error: passing 'const ClassB' as 'this' argument of 'ClassA* ClassB::child()' discards qualifiers [-fpermissive]
Now I am passing my classes to functions as pointers. I plan on always using pointers because I have a problem with the reference/pointer options. I'm choosing one, pointers, and sticking with it. So doSomethingA and doSomethingB I want that to be const to tell the programmer that their class is not being altered. But I only want one version of child() which I want to use sometimes as "read only" and other times allow the user to change the data within the child object (not the best method, I grant that, but there are some use cases where I need this). I even tried:
doSomethingA(const_cast<const ClassA*>(o->child()));
But that did not work.
In the past I removed the const declarations in the functions to make something like this work but now I want to use proper c++. Help please.

try
ClassA* child() const { return &m_child; }
or
const ClassA* child() const { return &m_child; }
to keep the const correctness
Also, you don't need to use pointers as long as you don't plan passing nullptr. So you can do the following:
void doSomethingB(const ClassB& o);
// in class ClassB
const ClassA& child() const { return m_child; }
ClassA& child() { return m_child; }
References still alow polymorphic stuff same way as pointers.

You're attempting to access a non-const function against a const object. You need to make the function const :
const ClassA* child() const { return &m_child; }
You can also provide a const and non-const version:
ClassA* child() { return &m_child; }
const ClassA* child() const { return &m_child; }
This way you can call non-const methods on ClassA when you have a non-const object.

Related

Overload += for a template?

I have a base class Animal and a derived class Bird : Animal. I use a template class that will store vectors of pointers to either Animal or Bird objects. I want to overload the += operator in such a way that I can insert a new animal right in the Atlas, so m_length = m_length + 1, pages.push_back(animal), just to get the idea.
Here's my template class:
template <class T>
class Atlas2 {
public:
int m_length;
std::list<T> pages;
Atlas2() { m_length = 0; }
~Atlas2() {}
void adauga(T data);
T operator+=(const T& data) {
this->m_length++;
this->pages.push_back(data);
return *this;
};
};
And here's the Animal/Bird classes:
class Animal {
protected:
std::string m_name;
public:
Animal() {}
Animal(std::string name) : m_name{name} {}
virtual void set_name(std::string name) { m_name = name; }
virtual std::string get_name() { return m_name; }
virtual std::string regn() const { return "???"; }
virtual ~Animal() { cout << "Destructor animal" << '\n'; }
};
class Bird : public Animal {
public:
bird() : animal() {}
bird(std::string name) : Animal{name} {}
void set_name(std::string nume) { m_name = nume; }
std::string get_name() { return m_name; }
std::string regn() const override { return "pasare"; }
~bird() { cout << "destructor pasare" << '\n'; }
};
However, I can't figure this out. When I use the overloaded += operator in main() like this:
Pasare *c = new Pasare{"vulture"};
Atlas2<Animal *> Atlas;
Atlas += c;
It shows me an error, that it couldn't convert Atlas<Animal *> to <Animal*>.
How should I implement this correctly? Any tip?
Note: The template works fine, I can store in my list pointers to either Animal or Birds without problems, and access their specific methods. I just can't figure out the += part.
You should return Atlas2<T> & not T:
Atlas2<T>& operator+=(const T& data) {
this->m_length++;
this->pagini.push_back(data);
return *this;
};
The basic problem is that you've declared your operator+= as returning a T, but the return statement in it is return *this;, which is an Atlas2<T>.
If you change the return type to Atlas2<T> &, it should work. That's what you would normally want to return from an operator+= anyways, though with your use, it doesn't matter much as you're ignoring the returned value.

Accessing member in nested composition in C++

If I have a class that contains another class (through composition) which in turn, contains another class. For example: a Teacher class containing a PersonalDetails class, that contains a ContactInformation class.
ContactInformation class:
class ContactInformation
{
private:
std::string m_email{};
std::string m_phoneNumber{};
public:
ContactInformation(const std::string &email, const std::string &phone)
: m_email{ email }, m_phoneNumber{ phone }
{
}
// Solution 1
const std::string& getEmail() const { return m_email; }
const std::string& getPhoneNumber() const { return m_phoneNumber; }
// Solution 2
const ContactInformation& getContactInfo() const { return *this; }
// Solution 3
friend class Teacher;
};
PeronalDetails class:
class PersonalDetails
{
private:
ContactInformation m_contact;
std::string m_name;
public:
PersonalDetails(const ContactInformation &info, const std::string &name)
: m_contact{ info }, m_name{ name }
{
}
// Solution 1
const std::string& getEmail() const { return m_contact.getEmail(); }
const std::string& getPhoneNumber() const { return m_contact.getPhoneNumber(); }
const std::string& getName() const { return m_name; }
// Solution 2
const ContactInformation& getContactInfo() const { return m_contact.getContactInfo(); }
const PersonalDetails& getPersonalDetails() const { return *this; }
// Solution 3
friend class Teacher;
};
Teacher class:
class Teacher
{
private:
PersonalDetails m_details;
double m_salary{};
public:
Teacher(const PersonalDetails &details, double salary)
: m_details{ details }, m_salary{ salary }
{
}
// Solution 1
const std::string& getEmail() const { return m_details.getEmail(); }
const std::string& getPhoneNumber() const { return m_details.getPhoneNumber(); }
const std::string& getName() const { return m_details.getName(); }
double getSalary() const { return m_salary; }
void printEmail1() const
{
std::cout << getEmail() << '\n';
}
// Solution 2
const ContactInformation& getContactInfo() const { return m_details.getContactInfo(); }
const PersonalDetails& getPersonalDetails() const { return m_details.getPersonalDetails(); }
void printEmail2() const
{
std::cout << getContactInfo().getEmail() << '\n';
}
// Solution 3
const std::string& getTeacherEmail(const ContactInformation &c) const
{
return c.getEmail();
}
void printEmail3() const
{
std::cout << getTeacherEmail(getContactInformation());
}
};
What is the "proper way" for the Teacher class to access the members (m_email & m_phoneNumber) in ContactInformation (the most "nested" class)?
Neither of the solutions I can come up with seem all that great.
Solution 1; is to have getters methods for the member variables in all of the classes. But this seems like a bad idea since the Teacher class will end up with a lot of getters methods. Especially if I were to add more classes in the future.
Solution 2; is to return the instance itself. I don't know if this is better or if it breaks any best practices. But you can use the instance in the Teacher class to call getEmail() on it.
Solution 3; is using friend classes (don't have a lot of experience using them). but since you still have to pass an instance of ContactInformation in order to get m_email. It doesn't seem much different from Solution 2.
Is there any way of making the Teacher class a friend (or something) with the ContactInformation class so I can do something like this:
teacher.getEmail();
Without having to have any getters except from the one in ContactInformation?
The problem with friends classes is that you will lose the posibility (in a future) of using ContactInformation for a different class than Teacher without really gaining much from that.
If PeronalDetails is a member of Teacher and ContactInformation is a member of PeronalDetails. you could simply teacher.personalDetails.contactInformation.m_email which is quite long and requires all these members being public.
A midlle point can be a personalized getter:
public:
Teacher::getEmail(){
return personalDetails.contactInformation.m_email;
}

Forcing class instances to be const

Is there any way to force to only allow const instances of class to be instantiated, and have non-const instances be detected as an error by the compiler?
is there any generic way to take an existing class, and "constify" it by removing all non-const functionality?
One possible workaround is to create a wrapper class that holds an instance of the class and only gives access to a const reference to it.
template<class T>
class Immutable {
public:
template<typename... Args>
Immutable(Args&&... args) : instance(forward<Args>(args)...) {
}
operator const T&() {
return instance;
}
const T& get() const {
return instance;
}
private:
Immutable& operator=(const Immutable& other) = delete;
T instance;
};
Suppose you have a mutable class Class:
class Class {
public:
Class() : m_value(0) {
}
Class(const Class& other) : m_value(other.m_value) {
}
Class(int value) : m_value(value) {
}
Class(int x, int y) : m_value(x + y) {
}
void change(int value) {
m_value = value;
}
int value() const {
return m_value;
}
private:
int m_value;
};
Here is how Immutable<Class> can be used:
void functionTakingConstReference(const Class& x) {
}
void functionTakingNonConstReference(Class& x) {
}
void functionTakingImmutableClass(Immutable<Class>& x) {
}
void functionTakingValue(Class x) {
}
int main(int argc, char *argv[])
{
// Any constructor of Class can also be used with Immutable<Class>.
Immutable<Class> a;
Immutable<Class> b(1);
Immutable<Class> c(2, 3);
Immutable<Class> d(c);
// Compiles and works as expected.
functionTakingConstReference(a);
functionTakingImmutableClass(a);
functionTakingValue(a);
cout << a.get().value() << endl;
// Doesn't compile because operator= is deleted.
// b = a;
// Doesn't compile because "change" is a non-const method.
// a.get().change(4);
// Doesn't compile because the function takes a non-const reference to Class as an argument.
// functionTakingNonConstReference(a);
return 0;
}
Is there any way to force to only allow const instances of class to be instantiated, and have non-const instances be detected as an error by the compiler?
No.
But you can declare all members as const. Then both const and non-const instances would behave largely in the same way and it shouldn't matter whether the instances are const.
I think you are looking for an immutable class. An easy way to get immutability is to declare all member variables as const. This way you ensure that the state of your objects will not change after construction.
This guarantee is independent of whether your object is const and even if you have non-const member functions for that class.
For example:
class Foo
{
public:
Foo(int id, double value) : m_id(id), m_value(value) { }
int Id() const { return m_id; }
double Value() const { return m_value; }
private:
const int m_id;
const double m_value;
};
Another way you could generate an immutable object of any type is through a template class. Something like this:
class Bar
{
public:
Bar(int id, double value) : m_id(id), m_value(value) { }
int Id() const { return m_id; }
double Value() const { return m_value; }
void SetId(int id) { m_id = id; }
private:
int m_id;
double m_value;
};
template<typename T>
class MyImmutable
{
public:
const T m_obj;
MyImmutable(const T& obj) : m_obj(obj)
{ }
};
int main()
{
cout << "Hello World!" << endl;
Foo a(1,2.0);
Bar x(2,3.0);
MyImmutable<Bar> y(x);
cout << "a.id = " << a.Id() << endl;
cout << "a.value = " << a.Value() << endl;
cout << "y.id = " << y.m_obj.Id() << endl;
cout << "y.value = " << y.m_obj.Value() << endl;
y.m_obj.SetId(2); // calling non-const member throws an error.
return 0;
}

Accessing list of fields and types in a class in c++

Hi i am trying to create a simple ORM in c++ for a project. For this example assuming a simple class as
class userProfile: public BaseOrm
{
public:
string username;
string email;
};
Now base orm has a method save() and migrate(). What i want is when a person calls migrate() all the schema , in this case username and email are populated as db tables and on save they persist on database.
What i am having problem with is how do i get what all fields are defined in the class, like in this example username and email and also there types, string in this case. Any help would be appreciated.
I know there is no reflection in c++, so i don't actually care about the variable name but more on the number of variables and there types to map them with DB.
adding reflection to c++ is not insanely difficult but it does require a reasonably good knowledge of template type deduction and some careful planning.
In this working example I have made a start for you. This framework supports writing the members out to a "statement" class (modelling a database prepared statement).
Similar techniques can be used to build out the SQL generation for CRUD.
No doubt there are already libraries that do this for you...
#include <iostream>
#include <iomanip>
#include <string>
#include <tuple>
#include <utility>
using namespace std;
struct statement
{
void setString(int index, const std::string& value)
{
std::cout << "setting index " << index << " to value " << std::quoted(value) << std::endl;
}
};
struct BaseOrm
{
virtual void serialise(statement& stmt) const = 0;
};
template<class Class>
struct class_tag {
using type = Class;
};
template<const char* Name>
struct name_tag {
static constexpr const char* name() { return Name; }
};
namespace detail {
struct reflection_item_concept
{
virtual const std::string& name() const = 0;
virtual std::string to_archive_string(const void* object) const = 0;
virtual void from_archive_string(void* object, const std::string& as) const = 0;
};
template<class T>
std::string to_archive_string_impl(const T& val) {
return std::to_string(val);
}
const std::string& to_archive_string_impl(const std::string& s) {
return s;
}
template<class NameTag, class Class, class Type>
struct reflection_item : reflection_item_concept
{
reflection_item(Type Class::* mfp) : mfp(mfp) {}
static const class_tag<Class> class_info() { return {}; };
static const char* raw_name() { return NameTag::name(); };
// concept implementation
const std::string& name() const override {
static const std::string s = raw_name();
return s;
}
std::string to_archive_string(const void* object) const override
{
auto& val = (*reinterpret_cast<const Class*>(object)).*mfp;
return to_archive_string_impl(val);
}
void from_archive_string(void* item, const std::string& as) const override
{
// similar mechanism here
}
Type Class::* mfp;
};
}
template<class NameTag, class Class, class Type>
constexpr auto reflection_item(NameTag, Type Class::* mp)
{
return detail::reflection_item<NameTag, Class, Type> { mp };
}
struct class_reflection_concept
{
virtual void serialise(const void* object, statement& stmt) const = 0;
};
namespace detail {
template<class ClassTag, class...ReflectionItems>
struct reflection_impl : class_reflection_concept
{
reflection_impl(ReflectionItems...refs)
: _reflectors(std::make_tuple(refs...))
{}
template<std::size_t...Is>
void serialise_impl(std::index_sequence<Is...>, const void* object,
statement& stmt) const
{
using expand = int[];
void(expand{
0,
(stmt.setString(Is + 1, std::get<Is>(_reflectors).to_archive_string(object)),0)...
});
}
void serialise(const void* object, statement& stmt) const override
{
serialise_impl(std::make_index_sequence<sizeof...(ReflectionItems)>(),
object, stmt);
}
std::tuple<ReflectionItems...> _reflectors;
};
}
template<class ClassTag, class...ReflectionItems>
auto& make_reflection(ClassTag tag, ReflectionItems...items)
{
static const detail::reflection_impl<ClassTag, ReflectionItems...> _ { items... };
return _;
}
const char txt_username[] = "username";
const char txt_email[] = "email";
const char txt_x[] = "x";
class userProfile: public BaseOrm
{
public:
string username = "test username";
string email = "noone#nowhere.com";
int x = 10;
// implement serialisation
void serialise(statement& stmt) const override
{
reflection.serialise(this, stmt);
}
static const class_reflection_concept& reflection;
};
const class_reflection_concept& userProfile::reflection =
make_reflection(class_tag<userProfile>(),
reflection_item(name_tag<txt_username>(), &userProfile::username),
reflection_item(name_tag<txt_email>(), &userProfile::email),
reflection_item(name_tag<txt_x>(), &userProfile::x));
int main()
{
userProfile x;
statement stmt;
x.serialise(stmt);
}
expected results:
setting index 1 to value "test username"
setting index 2 to value "noone#nowhere.com"
setting index 3 to value "10"
What I understand is that you want a generic behaviour for classes which have a variable set of fields.
I suggest you to create a "field" interface which will be stored in your base class with a container (for example a map of [fieldName, fieldInterface]). You still have to implement a behaviour for each field's type, but then you can create any class derived from the base class which have a dynamic set of field.
Here is an example :
#include <iostream>
#include <map>
using namespace std;
//the "Field" interface
class IFieldOrm
{
public:
virtual ~IFieldOrm() {}
virtual void save() = 0;
virtual void migrate() = 0;
};
//your base class
class BaseOrm
{
public:
virtual ~BaseOrm();
virtual void save();
virtual void migrate();
protected:
map<string, IFieldOrm*> m_fields; //prefer a smart pointer if you don't want to mess with raw pointer
};
//base class implementation
void BaseOrm::save()
{
for(auto& f : m_fields)
f.second->save();
}
void BaseOrm::migrate()
{
for(auto& f : m_fields)
f.second->migrate();
}
//don't forget to free your "fields" pointers if you have raw pointers
BaseOrm::~BaseOrm()
{
for(auto& f : m_fields)
delete f.second;
}
//then implement your basic types
//(like string, int, ..., whatever type you want to store in your database)
class StringFieldOrm : public IFieldOrm
{
public:
StringFieldOrm(const string& value) : m_value(value) {}
virtual void save();
virtual void migrate();
private:
string m_value;
};
void StringFieldOrm::save()
{
cout << "Save value " << m_value << endl;
//save stuff...
}
void StringFieldOrm::migrate()
{
cout << "Migrate value " << m_value << endl;
//migrate stuff...
}
class IntFieldOrm : public IFieldOrm
{
public:
IntFieldOrm(int& value) : m_value(value) {}
virtual void save();
virtual void migrate();
private:
int m_value;
};
void IntFieldOrm::save()
{
cout << "Save value " << m_value << endl;
//save stuff...
}
void IntFieldOrm::migrate()
{
cout << "Migrate value " << m_value << endl;
//migrate stuff
}
//and finally implement your final class
//note that this object can be "dynamically extended" by inserting new fields,
//you may want to prevent that and I can think of a solution if you want to
class UserProfile: public BaseOrm
{
public:
UserProfile(const string& username, const string& email, int age);
};
UserProfile::UserProfile(const string& username, const string& email, int age)
{
m_fields["username"] = new StringFieldOrm(username);
m_fields["email"] = new StringFieldOrm(email);
m_fields["age"] = new IntFieldOrm(age);
}
int main(int argc, char* argv[])
{
UserProfile user = UserProfile("Batman", "bw#batmail.com", 30);
user.save();
return 0;
}
create a userProfile variable and access them:
userProfile user;
int main(){
std::cout << user.username;
std::cout << user.email ;
}
this is how you would access them, except for different reasons, not printing them to the screen.

Can I combine setter and getter in one method, in C++?

I would like to combine setter/getter in one method, in C++, in order to be able to do the following:
Foo f;
f.name("Smith");
BOOST_CHECK_EQUAL("Smith", f.name());
I don't know how can I declare such a method inside Foo class:
class Foo {
public:
// how to set default value??
const string& name(const string& n /* = ??? */) {
if (false /* is it a new value? */) {
_name = n;
}
return _name;
}
private:
string _name;
}
I'm looking for some elegant solution, with a true C++ spirit :) Thanks!
class Foo {
public:
const string& name() const {
return name_;
}
void name(const string& value) {
name_ = value;
}
private:
string name_;
};
You can create a second method with different parameters, in this case none to simulate a default parameter:
string& name() {
// This may be bad design as it makes it difficult to maintain an invariant if needed...
// h/t Matthieu M., give him +1 below.
return _name;
}
And if you need a const getter, just add it as well!
const string& name() const {
return _name;
}
The compiler will know which one to call, that's the magic of overloading.
Foo f;
f.name("Smith"); // Calls setter.
BOOST_CHECK_EQUAL("Smith", f.name()); // Calls non-const getter.
const Foo cf;
BOOST_CHECK_EQUAL("", cf.name()); // Calls const getter.
I would not advise trying to do this, because then you can't make your "get" functions const. This would work, but it would totally break when someone has a const Foo and wants to execute GetA(). For that reason, I advise separate functions and a const GetA().
class Foo
{
int _a;
static int _null;
public:
const int& a(const int& value = _null) {
if (&value != &_null)
_a = value;
return _a;
}
};