I am currently learning c++ and I am an experienced C# and Java developer.
I have a class B which contains a member of class A, and I want users of class B to be able to change values in class A but not be able to change the instance of class A itself.
Basically it want to prevent b.getA() = anotherA; being allowed.
Is this possible in c++ or is my design completely wrong here?
This is my small c++ program
#include <string>
#include <iostream>
using namespace std;
class A {
public:
string getName()
{
return name;
}
void setName(string value)
{
name = value;
}
private:
string name = "default";
};
class B {
public:
A &getA()
{
return anInstance;
}
private:
A anInstance;
};
int main(int argc, char** argv) {
B b;
cout << b.getA().getName() << std::endl; // outputs "default"
b.getA().setName("not default");
cout << b.getA().getName() << std::endl; // outputs "not default"
A a;
a.setName("another a instance");
b.getA() = a; // I want to prevent this being possible
cout << b.getA().getName() << std::endl; // outputs "another a instance"
}
A C# example of what I am trying to do
class Program
{
class A
{
private string name = "default";
public string getName()
{
return name;
}
public void setName(string value)
{
name = value;
}
}
class B
{
private A anInstance;
public A getA()
{
return anInstance;
}
}
static void Main(string[] args)
{
B b = new B();
Console.WriteLine(b.getA().getName()); // outputs "default"
b.getA().setName("not default");
Console.WriteLine(b.getA().getName()); // outputs "not default"
}
}
You wrote:
A a;
a.setName("another a instance");
b.getA() = a; // I want to prevent this being possible
I ask, why?
Why do you want to prevent that?
Your next line is:
cout << b.getA().getName() << std::endl; // outputs "another a instance"
But that is misleading, you have not changed the instance of A inside b, you've only changed b.anInstance to be a copy of a. In other words, you've changed the name to say "another a instance" but that doesn't mean it's true. It's no more true that it's another instance than if you called b.getA().setName("another a instance") (in fact, the result is identical to doing that!)
Try it:
A a;
a.setName("another a instance");
std::cout << &b.getA() << std::endl;
b.getA() = a;
std::cout << &b.getA() << std::endl;
You'll get the same address printed both times, because b.getA() = a does not replace b.anInstance, it just modifies it, just like calling setName does.
That's because in C++ B::anInstance is not just a reference to an A, it is an A, and so by assigning to it you don't change the reference to point to a different A, you change the A itself.
So, to go back to your original question, since the thing you're worried about doesn't happen anyway why do you need to prevent it? If you already allow b.anInstance to be modified via the setName() function, why not just let it be modified by assignment as well?
If the answer to that question is that there are other properties of A which you don't want to be changed, and the assignment would change them, then instead of exposing the whole A object via B::getA() just add a new member function to B which sets the name. Doing this is better encapsulation than simply exposing the entire A object anyway. Too often Java and C# seem to encourage bad designs involving getters and setters for everything, which is pointless and encapsulates nothing; you might as well just make every member public and access them directly if there is a setter for everything.
If you want a B that contains an A that doesn't change except for its name, then don't expose the whole A, just provide a setter on the outer object:
class A {
public:
string getName() const // N.B. Added 'const' here
{
return name;
}
void setName(string value)
{
name = value;
}
private:
string name = "default";
};
class B {
public:
// Read-only access to the contained object:
const A& getA() const
{
return anInstance;
}
// Update the name of the contained object:
void setName(string value)
{
anInstance.name = value;
}
private:
A anInstance;
};
When you allow general non-const access to a member (such as A B::anInstance), then this implies access to all the (public) members of that member (const and not). In particular, you provide access to the operator=, which allows the contents of the member to be changed. Of course, it's still the same A (with the same address), but its data have changed.
In your C# code you are actually never allowing/using non-const access to the B::anInstance, so your C++ is not really equivalent. Consider
class B
{
A anInstance; // private by default
public:
A const& getA() const { return anInstance; } // const access only
A & getA() { return anInstance; } // full access
A copyA() const { return anInstance; } // make a copy
};
The first getA() is accessible from const B and only allows const access to anInstance, i.e. only to const members of A. The second getA() is only accessible from non-const B and allows full (public) access to anInstance, including A::operator=(A const&) (if present, i.e. either declared or not =delete in the definition of class A).
Finally, copyA() provides no access whatsover to B::anInstance, but returns merely a copy to it. Typically (if A is non-trivial and/or large) this requires much more effort than merely returning a reference (like a pointer), but in terms of usage/effect, it is very similar to getA() const (it's different if some const member of A actually change the state of A or if you use the dreaded const_cast<> as in const_cast<A&>(b.getA())=otherA).
You might want to use const pointer instead of reference, but it can have a lot of side effects:
class A {
public:
string getName() const
{
return name;
}
void setName(string value)
{
name = value;
}
private:
string name;
};
class B {
public:
A * const getA() const
{
return anInstance;
}
private:
A* anInstance;
};
int main(int argc, char** argv) {
B b;
cout << b.getA()->getName() << std::endl; // outputs "default"
b.getA()->setName("not default");
cout << b.getA()->getName() << std::endl; // outputs "not default"
A a;
a.setName("another a instance");
b.getA() = a; // I want to prevent this being possible
cout << b.getA()->getName() << std::endl; // outputs "another a instance"
}
As #Captain Price mentioned you can prohibit =operator of A class:
class A {
public:
string getName()
{
return name;
}
void setName(string value)
{
name = value;
}
private:
string name;
A& operator=(const A& other);
};
class B {
public:
A &getA()
{
return anInstance;
}
private:
A anInstance;
};
int main(int argc, char** argv) {
B b;
cout << b.getA().getName() << std::endl; // outputs "default"
b.getA().setName("not default");
cout << b.getA().getName() << std::endl; // outputs "not default"
A a;
a.setName("another a instance");
b.getA() = a; // I want to prevent this being possible
cout << b.getA().getName() << std::endl; // outputs "another a instance"
}
explicitly disable value-copying!
private:
A(const A&);
A& operator=(const A&);
Related
I'm coming from C# to C++ and when I try to construct tests, copy constructors are making it hard for me to mock the usual way I am accustomed to.
#include <iostream>
using namespace std;
class DB {
public:
virtual int getValue() { return 42; }
DB(const DB& source) { }
DB() { }
};
class DB_Mock: public DB {
public:
virtual int getValue() { return 999; }
};
class A {
public:
A(DB db) {
m_db = db;
}
void doIt() {
cout << "Result:" << m_db.getValue() << endl;
}
private:
DB m_db;
};
int main() {
/* prints 42 as expected */
DB db;
A a(db);
a.doIt();
/* prints 42, expected 999 */
DB_Mock db_mock;
A a2(db_mock);
a2.doIt();
return 0;
}
How do you approach problems like this?
m_db is not a reference or pointer, c++ polymorphism is work with only base class pointer or reference.
A(DB db) {
m_db = db; // m_db just sliced copy of base part of db.
}
Change this to reference like
class A
{
public:
explicit A(DB &db) : m_db(db) // it is initializing not copy
{
}
void doIt()
{
std::cout << "Result:" << m_db.getValue() << std::endl;
}
private:
DB &m_db; // Now m_db is reference
};
Compile and Run Here
Above will work But, You can not change reference value if you want to change use smart pointer[Recommended].
Reference :
Constructors and member initializer lists
Smart pointer
override
See virtual table to understand v_ptr
Edit:
As Pete Becker said be aware of lifetimes, if you pass local variable reference like
A make()
{
DB db;
A a(db);
return a;
}
int main()
{
const A &a = make();
// Wrong db is destroyed (dangling reference).
a.doIt(); // assume doIt(), getValue() are declared as const
return 0;
}
Above code is Wrong (undefined behaviour), So i Recommend to use smart pointer(Heap storage) to avoid this problems.
Thanks.
For an example, we were given sample code where we had to find its errors and give explanations as to why there were errors. The questions I have are numbered 1-5.
First part
We had a class, Flight, declared as follows:
class Flight {
public:
~Flight(){for(auto & b: places) delete b; }
....
..
private:
vector<unique_ptr<Bill>>places; //bill is a previously declared class
};
I said that is because b is a unique_ptr and we can't delete them, they are deleted by default. So my suggestion was that we could either change the attribute and take normal points in C, vector<Bill*>places; or just remove the destructor. However, someone suggested that we write instead b.reset();
Can someone explain to me what is the purpose of that as opposed to just removing the destructor? Which is better to do, and why?
Second part
There are other conceptual mistakes, and I am not sure if I am right. The rest of the code is as follows:
class Sell {
public:
virtual ~ Sell(){}
virtual double price() const = 0;
protected:
double base_price;
};
class Bill : public Sell {
public:
Bill(int id, double price, string name, bool vegetarian) etc{}
virtual ~Bill()
{}
virtual ostream& display(ostream& out) const
out << "no. " << id << " for " << name << endl; return out;
}
double price() const{
if(vegetarian) { return base_price() - 20; }
else { return base_price(); }
}
private: int id; string name; bool vegetarian;
};
ostream& operator << (ostream& out, const Bill& bill){ return bill.display(out); }
class Business : public Bill {
public: Business(int id, double base_price.........)etc
virtual ~Business() { ~Bill(); }
ostream& display(ostream& out ) const {
out << "business ";
Bill::display(out);
if (extra_space){ out << "with supplementary space. " ; }
return out;
}
double price() {
double p(Bill::price());
if(extra_space) { p*= 1.5; }
return p;
}
private: bool extra_space;
};
//then there is class Flight as defined above.
class Flight {
public:
~Flight(){for(auto & b: places) delete b; }
void sold(Bill* b){ places.push_back(unique_ptr<Bill>(b)); }
void display() const {
for(auto const& b: places){cout << "The Bill " << *b << "is " << b->price() << "Dollars. " << endl;
}}
..
private:
vector<unique_ptr<Bill>>places; //bill is a previously declared class
};
int main(){
Flight f1;
f1.sold(new Bill(1,100,"Chopin", true));
f1.sold(new Business(2, 100, "Sand, false, true)):
f1.display() << endl;
return 0;
}
There is a warning that says virtual double Bill::price() const was hidden. I suggested this is masking and that the price function in the bill class should be virtual. Does this make sense?
There was also a warning by double Business::price() [Woverloaded-virtual]. This, I don't understand, is this because there is no const in the price() class defined in business?
Also, is price() considered as the same function in all the classes? Because I don't understand how if there is a missing const in the price of business, that it would compile. I would have thought it would not only display a warning but a compilation error...
Finally, for the main() function, there is a problem with (new Business), is this because we can only create an address for bill?
Almost all classes, if they are written in c++11 or better and are designed properly will not have user-defined destructors, user-defined copy operators or user-defined move operators.
There is a very good reason for this, which is that if you don't define any of these, the compiler will generate correct versions for you.
In your case, the vector of Bill is managing the lifetime of the bills through std::unique_ptr (good!!). This means that lifetimes will be correctly managed automatically and your first intuition was correct - simply remove the destructor and let the compiler do the right thing.
Your friend's suggestion to replace delete with .reset() was also correct, but convoluted and un-necessary.
#include <iostream>
using namespace std;
class animal
{
public:
void breathe()
{
cout << "breathe!" << endl;
}
int height;
};
class fish : public animal
{
public:
void breathe()
{
cout << "fish breathe!" << endl;
}
int weight;
};
int main()
{
animal *p_animal = new animal();
fish *p_fish = (fish *)p_animal;
p_fish->breathe();
p_fish->weight = 2;
cout << p_fish->weight; //I new a animal instance,but why does it has weight property?
int temp;
cin >> temp;
}
As various commenters have pointed out, you're tricking the compiler into letting you do this.
The key line is
fish *p_fish = (fish *)p_animal;
This line basically forces the compiler to accept that whatever p_animal was pointing to, it's now pointing to a fish.
If you are able to access the properties there, then it's basically chance, and a different compiler might give you different results.
If you had written
fish *p_fish = p_animal;
Then compiler would have complained.
Some side comments on your code.
In this example, you probably want virtual functions. See this modification of your code with some comments :
#include <iostream>
class Animal
{
public:
virtual ~Animal() {} // Always define virtual destructor in this case
virtual void breathe() const // This fonction doesn't modify the object
{ // then, add 'const' at the end
std::cout << "breathe!" << std::endl;
}
unsigned int height; // An animal can't have a negative height ;-)
// then, use an unsigned type.
};
class Fish : public Animal
{
public:
virtual ~Fish() {}
virtual void breathe() const
{
std::cout << "fish breathe!" << std::endl;
}
unsigned int weight;
};
int main()
{
Animal* p_animal = new Animal; // Don't put () for empty constructor
Animal* p_fish = new Fish;
p_animal->breathe(); // this prints "breathe!"
p_fish->breathe(); // this prints "fish breathe!" even if the pointer is
// an pointer to Animal. It's the behaviour of virtual
// functions : when the code is executed, the real type
// is checked and the corresponding function is called.
p_fish->height = 10; // This works
// p_fish->weight = 5; // This doesn't compile, it's an pointer to Animal
Fish* p_real_fish = dynamic_cast<Fish*>(p_fish);
p_real_fish->height = 10; // This works
p_real_fish->weight = 5; // This works too
delete p_animal; // Don't forget to release the memory !
delete p_fish;
}
I hope this can help you.
I created some code to reproduce the problem:
#include "stdafx.h"
#include <iostream>
#include <vector>
class A
{
protected:
int m_X;
public:
A() {
std::cout << "in A ctor" << std::endl;
m_X = 0;
}
virtual void printX(){ std::cout << "in A " << m_X << std::endl; }
};
class B : public A
{
public:
B() {
std::cout << "in B ctor" << std::endl;
m_X = 1;
}
virtual void printX(){ std::cout << "in B " << m_X << std::endl; }
};
class As
{
public:
void AddA( const A &a ){ m_As.push_back( a ); }
void PrintXs()
{
for ( auto a : m_As )
{
a.printX();
}
}
private:
std::vector<A> m_As;
};
int _tmain(int argc, _TCHAR* argv[])
{
As as;
B b;
as.AddA( b );
as.PrintXs();
system("pause");
return 0;
}
The output of this is:
in A ctor
in B ctor
in A 1
I want "in B 1" instead of "in A 1". I'm sure my understanding of virtual is flawed. How must I change the code to call the B PrintX()? Note that there will be other classes that inherit from A so I really don't want to code a static call.
Thanks.
What you're doing is called slicing. This is where you take an object of a derived class and trim off everything that is not in the parent and assign it to the parent.
What you want to do is use polymorphism to do what you explained. To do this, change your vector from a copy of the object, to a ptr to the object.
If interested in more details, please use the links provided, the information included in them seems to be very complete.
The quick fix is to change your As class to the following:
class As
{
public:
void AddA( A &a ){ m_As.push_back( &a ); }
void PrintXs()
{
for ( auto a : m_As )
{
a->printX();
}
}
private:
std::vector<A*> m_As;
};
When you use std::vector<A> m_As;, the vector can only fit A objects. If you use pointers instead then polymorphism can work and call the correct printX function. However, this has the problem of dangling pointer if the lifetime of the pointed to object expires. To handle that it would be better to use a smart pointer class like std::unique_ptr.
Since you're passing objects by value you can not take advantages of polymorphism. Pass them by (smart) pointers or references.
std::vector<std::shared_ptr<A>> m_As;
// or
std::vector<std::unique_ptr<A>> m_As;
// or
std::vector<A*> m_As; // be careful of bare pointers
// or (since C++11)
std::vector<std::reference_wrapper<A>> m_As;
std::reference_wrapper magic!
For the last one, you can use std::reference_wrapper and std::ref:
class As
{
public:
void AddA(A &a){ m_As.push_back( std::ref(a) ); }
void PrintXs() const
{
for ( auto a : m_As )
{
a.get().printX();
}
}
private:
std::vector<std::reference_wrapper<A>> m_As;
};
Using last code, you don't have to change main code.
Live code
for ( const auto & a : m_As )
{
a.printX();
}
it will keep you from expanded copy and provide the B-instance instead of A-instance, appeared as copy.
Consider the following code:
#include <iostream>
#include <map>
class Value
{
public:
void set(const int intValue){ intValue_ = intValue; }
int read() const { return intValue_; }
void replaceIfInMap(){
std::map<int,int>::iterator it;
it = valuesToReplace_->find(intValue_);
if(it != valuesToReplace_->end()){
intValue_ = it->second;
}
}
Value(std::map<int,int>* valuesToReplace) : valuesToReplace_(valuesToReplace){}
private:
std::map<int,int>* valuesToReplace_;
int intValue_;
};
class Holder {
public:
void doStuffWithValues(){
Value a(&valuesToReplace_), b(&valuesToReplace_), c(&valuesToReplace_);
a.set(1); b.set(2); c.set(3);
valuesToReplace[2]=5;
a.replaceIfInMap(); b.replaceIfInMap(); c.replaceIfInMap();
std::cout << "a: " << a.read()
<< " b: " << b.read()
<< " c: " << c.read() << std::endl;
}
private:
std::map<int,int> valuesToReplace_;
};
int main()
{
Holder holder;
holder.doStuffWithValues();
}
How could I get access to the valuesToReplace_ member in a more convenient (and preferably more elegant) way? I have considered storing the map as a public static member of the class Value, but that would deny the possibility of having multiple instances of the Holder class, as each Holder instance requires a set of Value instances with different replacement settings.
A global map would be an even uglier "solution"...
Calling Value::read() from Holder and doing the map interaction there is not an option as this code is only a simplification and in the real code the equivalent of each instance of Value can store pointers to other instances of the same class rendering the aforementioned method overly complex and bulky.
Why does the above code even work? Holder::valuesToReplace_ is private! Is this just normal C++ behaviour (as you cannot get that pointer without access to the private members of the class anyway)?
Why does the above code even work? Holder::valuesToReplace_ is
private!
It is private, so Holder::doStuffWithValues can access it because it is a member function, nothing wrong there.
Value a(&valuesToReplace_), b(&valuesToReplace_), c(&valuesToReplace_);
a.set(1); b.set(2); c.set(3);
Here, all your Value objects have valuesToReplace_ pointing to the same map is that what you want? It seems strange, I would either have a static map (which would make a copy on assignment) or a smart pointer to prevent unexpected deletion (but allow NULL values).
How could I get access to the valuesToReplace_ member in a more
convenient (and preferably more elegant) way?
You could keep it private and have public member functions which return begin/end const_iterators for the map, or setIntForInt/getIntForInt accessor methods which are not dependent on internal implementation.
How about passing a reference to the valuesToReplace map to your replaceIfInMap method?
class Value
{
public:
void set(const int intValue){ intValue_ = intValue; }
int read() const { return intValue_; }
void replaceIfInMap(std::map<int,int> const& valuesToReplace_){
std::map<int,int>::const_iterator it;
it = valuesToReplace_->find(intValue_);
if(it != valuesToReplace_->end()){
intValue_ = it->second;
}
}
Value() {}
private:
int intValue_;
};
class Holder {
public:
void doStuffWithValues(){
Value a, b, c;
a.set(1); b.set(2); c.set(3);
valuesToReplace_[2]=5;
a.replaceIfInMap(valuesToReplace_);
b.replaceIfInMap(valuesToReplace_);
c.replaceIfInMap(valuesToReplace_);
std::cout << "a: " << a.read()
<< " b: " << b.read()
<< " c: " << c.read() << std::endl;
}
private:
std::map<int,int> valuesToReplace_;
};