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;
}
Related
I was trying to figure out this exercise from a school exam.
They implemented an abstract template Book class, and the assignment is to implement a bookshelf class.
I tried to construct a set of book pointers with a custom comparator, but then I encounter a compilation error:
In template: reference to type 'const Book<std::basic_string<char>>' could not bind to an lvalue of type 'const std::_Rb_tree<...>
(I implemented a sub class BOOK2 just for debugging purposes)
This is the long given book abstract class
#include <iostream>
#include <set>
#include <string>
#include <utility>
template <class T>
class Book
{
// any member variables are inaccessible to descendants
private:
std::string _title; // do not call a copy-ctr
T _author; // do not call a copy-ctr
size_t _number_of_pages;
public:
Book(std::string title,
T author,
size_t number_of_pages)
: _title(std::move(title)),
_author(std::move(author)),
_number_of_pages(number_of_pages)
{}
virtual ~Book() = default;
const std::string& get_title() const
{ return _title; }
const T& get_author() const
{ return _author; }
size_t get_number_of_pages() const
{ return _number_of_pages; }
public:
virtual Book<T>* clone() const = 0; // implemented *only* by descendent classes
virtual bool is_available_on(const std::string& platform) const = 0; // implemented *only* by descendant classes
protected:
virtual void do_output(std::ostream& os) const // can be overridden; can be accessed *only* by descendants
{
os << _title << ", " << _author << ", " << _number_of_pages << " pages";
}
// output should depend on who book really is
friend std::ostream& operator<<(std::ostream& os, const Book& book)
{
book.do_output(os);
return os;
}
};
This is what I implemented:
class Book2: public Book<std::string>{
public:
Book2(std::string &title,
std::string &author,
size_t number_of_pages)
: Book<std::string>(title,author,number_of_pages){}
bool is_available_on(const std::string &platform) const override{return
true;}
Book<std::basic_string<char>> * clone() const override{
Book<std::basic_string<char>> * a{};
return a;
}
};
template<class TP>
static bool book_comp(const Book<TP>& a,const Book<TP> & b){
return a.get_title()<b.get_title();}
template<class TT>
class Bookshelf
{
public:
typedef bool(*book_comp_t)(const Book<TT>& a,const Book<TT> & b);
// DO NOT CHANGE NEXT TWO LINES:
auto& get_books() { return _books; } // DO NO CHANGE
auto& get_books() const { return _books; } // DO NO CHANGE
Bookshelf():_books(book_comp<TT>){}
void add(Book<TT>& book)
{
size_t init_size=_books.size();
_books.insert (&book);
if(init_size==_books.size()){
throw std::invalid_argument("book already in bookshlf");
}
}
// sorted lexicographically by title
friend std::ostream& operator<<(std::ostream& os, const Bookshelf<TT>&
bookshelf)
{
for(const auto& book :bookshelf._books)
{
os << *book << std::endl;
}
}
private:
std::set<Book<TT>*,book_comp_t> _books;
};
int main ()
{
std::string a ="aba";
std::string bb ="ima;";
Book2 b = Book2(a, bb, 30);
Bookshelf<std::string> shelf;
std::cout<<b;
shelf.add(b);
}
I tried changing the const qualifiers in some places, and it didn't work.
I also tried without using the custom comparator function which worked ok.
I think this is probably some syntax error maybe?
std::set<Book<TT>*,book_comp_t> _books; is a set of Book<TT>*, and thus requires a comparator whose parameters are of type Book<TT>*, not const Book<TT>&
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.
I'm making a program that would essentially just be a manager for a record label (I'm just kind of making stuff up as I go along for fun), and I have a few classes. class RecordLabel, class Rapper, and class Album so far. Here's album:
using namespace std;
#include <iostream>
#include <string>
class Album
{
public:
void getAlbumName() const;
string setAlbumName(string);
void getNumTracks() const;
int setNumTracks(int);
void getTracklist() const;
string setTracklist(string);
private:
string albumName;
int numTracks;
string tracklist[];
};
So I'm just not sure about tracklist. My first idea was to use an array as you can see, but honestly I'm not sure what would be best. Could I use a linked list? Should I use an array of pointers? I've always been somewhat iffy on my knowledge of things like pointer arrays and lists so I figured this was a good opportunity to learn something.
Edit: I should be more clear: tracklist would just be a way to store the titles of however many tracks on an album.
Firstly, using namespace std can lead to subtle bugs, as mentioned by Max Vollmer in the comments.
I'd prefer setting to be done in the constructor, there won't be much reason to keep changing properties like Name.
This looks to be classic OOP, you have an 'Album' which is a collection of 'Tracks', and these tracks have both RecordLabel and Artist? Say, a Artist can be Emimem, type 'Rapper', genre 'Hip Hop'; and a RecordLabel can be Vanity, Open ect.
So, lets say, something like this -
class RecordLabel
{
LableType m_Type;
public:
RecordLabel():m_Type(LableType::None) {}
RecordLabel(LableType L)
{
m_Type = L;
}
};
class Artist
{
std::string m_Name;
ArtistType m_Type;
ArtistGenre m_Genre;
public:
Artist():m_Type(ArtistType::None), m_Genre(ArtistGenre::None) {}
Artist(const std::string Name, ArtistType Type, ArtistGenre Genre)
{
m_Name = Name; m_Type = Type; m_Genre = Genre;
}
};
class Track
{
std::string m_TrackName;
Artist m_Artist;
RecordLabel m_Lable;
public:
Track(const std::string Name, const Artist& A, const RecordLabel& R)
{
m_TrackName = Name;
m_Artist = A;
m_Lable = R;
}
friend ostream& operator<<(ostream& os, const Track& dt)
{
std::cout<<"TrackName: "<<dt.m_TrackName;
return os;
}
};
class Album
{
std::string m_albumName;
std::vector<Track> m_Tracks;
public: Album(const std::string& Name)
{
m_albumName = Name;
}
void AddTrack(const Track& T)
{
m_Tracks.push_back(T);
}
friend ostream& operator<<(ostream& os, const Album& dt)
{
cout<<"Album Name: "<<dt.m_albumName<<"\n";
for(auto It: dt.m_Tracks)
{
std::cout<<It<<"\n";
} return os;
}
};
Working code here.
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.
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;
}
};