0

I have a homework in which:

we have this code:

#include <iostream>
using namespace std;
class Test {
    int x;
    char y;
public:
    Test() :x(0), y(0) { ; }
};
int main() {
    Test t;
    //Do stuff!
    return 0;
}

and without adding getters and setters or using friend class we have to read x and y and also change them.

I searched and found these ways:

if there was a template function in my class I could say:

class Test {
    int x;
    char y;
public:
    Test() :x(0), y(0) { ; }
    template<typename T>
    void do_something() {//not necessarily void function
        //Do some stuff 
    };
};

class a;
// My specialization.
template <>
void Test::do_something<a>() {
    cout << x << endl;
    cout << y << endl;
    // getting data 
    x = 5;
    y = 'a';
    // changing data
    cout << x << endl;
    cout << y << endl;
    // getting data after changes we made
}

int main() {
    Test t;
    t.do_something<a>();
    return 0;
}

and also the method, which I think is this question answer, is using pointers.

like this:

class Test {
    int x;
    char y;
public:
    Test() :x(0), y('0') { ; }
};
int main() {
    Test t;
    int* ptr = (int*)&t;
    cout << "x = " << *ptr << " y = " << (char)*(ptr + 1) << endl;
    *ptr--;
    //getting data
    *ptr = 12;
    ptr++;
    *ptr = 65;
    //changing data
    ptr--;
    cout << "x = " << *ptr << " y = " << (char)*(ptr + 1) << endl;
    //getting data after changes we have made
    return 0;
}

or using reinterpret_cast and pointers:

struct pointer {
    int x;
    char y;
};

class Test {
    int x;
    char y;
public:
    Test() :x(0), y('0') { ; }
};
int main() 
{
    Test t;

    pointer* p = reinterpret_cast<pointer*>(&t);
    cout << "X = " << p->x << " Y = " << p->y << endl;
    //getting data
    p->x = 5;
    p->y = 'a';
    //changing data
    cout << "X = " << p->x << " Y = " << p->y << endl;

    //getting data from class after changing them with pointers
    return 0;
}

my questions are:

  1. is such thing possible in other object oriented languages?
  2. does this mean access modifiers are useless?
  3. and is there anything we can do to prevent such thing to happen?
hanie
  • 1,863
  • 3
  • 9
  • 19
  • 5
    the question is not clear. You say that you cannot modify `Test` but all your attempts modify `Test`. If you are allowed to modify it then you should write getters and setters (no template and definitely not `reinterpret_cast`) – 463035818_is_not_an_ai Jun 05 '20 at 08:55
  • 6
    is a traffic light useless? I mean when it is red I can still cross the street... Access specifiers are not there to prevent you from doing something wrong by all means, they are to help you to avoid doing something wrong (and if you try hard you can still do something wrong) – 463035818_is_not_an_ai Jun 05 '20 at 08:58
  • 2
    If you can add `do_something()` to `Test`, you can add `get_x()`, `get_y()`, `set_x()`, and `set_y()` instead. – Daniel Langr Jun 05 '20 at 09:08
  • @idclev463035818 sorry I edited my question and explained better. – hanie Jun 05 '20 at 09:17
  • @DanielLangr sorry I edited the question.and explained it better. – hanie Jun 05 '20 at 09:18
  • 2
    It is worth noting that the second approach is undefined. The language does not allow casting a `Test` pointer to an `int` pointer and then dereferencing this pointer. – Johan Jun 05 '20 at 09:18
  • 1
    Such an assignment **makes no sense**. Yes, you can [bypass access-specifiers](https://stackoverflow.com/q/424104/580083), but why would anyone want to do this? – Daniel Langr Jun 05 '20 at 09:21
  • @Johan is there a way to do it right? – hanie Jun 05 '20 at 09:25
  • 1
    the way to do it right is to not access private members or talk to the author of the class to change them to public when they should be public – 463035818_is_not_an_ai Jun 05 '20 at 09:26
  • @idclev463035818 I got your point about Access specifiers, but I have to right some code instead of `//do stuff` in `main` to read and change `x` and `y` ,so I'm looking for some ways to do. – hanie Jun 05 '20 at 09:31
  • Your class is _trivially-copyable_, so you can use `memcpy` with it. For example, copy the underlying bytes of `t` into a buffer, modify that buffer, and copy the contents back to `&t`. – Daniel Langr Jun 05 '20 at 09:38
  • it isnt completely clear what your assignment is. If it only says "and without adding getters and setters or using friend class we have to read x and y and also change them." I would either hand in a "solution" that says "not possible" or ask the tutor for clarification – 463035818_is_not_an_ai Jun 05 '20 at 09:44
  • As for `memcpy`, `Test` is also a _standard-layout_ type. Therefore, you can create a _layout-compatible_ class and `memcpy` between them. Just I am not sure whether your `struct pointer` is layout-compatible with `Test`. – Daniel Langr Jun 05 '20 at 10:12
  • 2
    Basically, this should work: `Test t; pointer p; memcpy(&p, &t, sizeof(Test)); p.x = 1; p.y = `1`; memcpy(&t, &p, sizeof(Test));`. – Daniel Langr Jun 05 '20 at 10:25

2 Answers2

3
  1. (with pointers) why this happen?

I don't understand this one, so I will skip it.

  1. is such thing possible in other object oriented languages?

Consider python. In python making something private is explicitly only an agreement between the author and the user, but nothing prevents a user from accessing private members. Though, they should not. C++ isn't that explicit about saying "if you want you can access private members", but still it is possible with some effort. Nevertheless you should not. C++ does not prevent you from shooting yourself in your foot and accessing private members is one way of doing that. It isn't the case in your example, but typically accessing private members directly will break the object beyond repair.

  1. does this mean access modifiers are useless?

I'll repeat my comment: Is a traffic light useless? I mean when it is red I can still cross the street. Access specifiers are not there to prevent you from doing something wrong by all means, they are to help you to avoid doing something wrong (and if you try hard you can still do something wrong).

  1. and is there anything we can do to prevent such thing to happen?

Declaring a member as private is enough to signal that a user should not access the member directly by any means. If someone wants to break that agreement then they can do it. You cannot prevent a user from doing something wrong. If they want to break your class they can do so. However, it is not your responsibility to guarantee that something broken still works as expected. If a user bypasses access specifiers then they broke the agreement between them and you. Consider you buy a laptop and throw it out of the window from 42th floor. Will you complain to the manufacturer that afterwards the laptop is not working properly anymore? I guess no, instead you will understand that you made something wrong with using your laptop.

PS: Your last two examples are undefined behavior. reinterpret_cast is not a way to cast between arbitrary types magically. The set of allowed casts and what you can do with the results is in fact rather limited (see here). Also a c-style cast enables you do to casts that can be very wrong, without your compiler complaining about it. Thats why they should be avoided in favor of the proper c++ casts (static_cast et al).

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • thanks, you explained it very well . this assignment is not completely clear to me also , but when I asked about it, I was told to search question title on internet and I found those codes. I have one other question is using `memcpy` as mentioned in comment also UB? – hanie Jun 05 '20 at 09:55
  • @hanie no, using `memcpy` is fine, the problem without your pointers is that you pretend to have a `pointer` object but there is none (you never created one). Only padding might become a problem – 463035818_is_not_an_ai Jun 05 '20 at 10:56
  • I have a question. I learned about using `reinterpret_cast` here:https://stackoverflow.com/a/8282638/12330258 .is it also wrong or am I doing it wrong? – hanie Jun 05 '20 at 14:37
  • @hanie that code can work with a particular compiler, but as far as the C++ standard is concerned it is not valid C++. – 463035818_is_not_an_ai Jun 05 '20 at 14:41
1

Answering your question 4, is there anything we can do to prevent such thing to happen?:

It is indeed a language design problem that code using a class in C++ is typically able to see the inner makeup of a class. A visible, complete class definition is clearly a breach of information hiding. It's necessary though because of the "by-value semantics" of C++ that it inherited from C and which distinguishes it from, say, C# or Java.

One of the consequences is what you describe: That users can easier access object data they are not opposed to. (To be fair, with enough malicious energy that is unpreventable in the general sense no matter the precautions, but knowing the class layout allows you to do so with less "criminal effort", in this case through normal language means. Another, even simpler way which I recall was buried in one large project when it went open source was to simply #define private public before including the header in question.)

A second, more relevant problem is that code which uses objects of that class, or one of its descendants, is too tightly coupled with that class; it knows more than it should or needs to. Any trivial change to the class makes it necessary to recompile all code which includes its definition, directly or indirectly. For large projects with elaborate class hierarchies touching a base class may cause a senseless re-build of the whole project.1

To finally answer your question: The canonical C++ strategy to reduce this coupling are compilation firewalls. You essentially define an interface of pure virtual functions and no data, which should be relatively stable. User code sees only that interface. By that you gain information hiding and the power of polymorphism. But because you cannot directly handle objects any longer but only pointers or references, you lose the advantages of C++'s by-value paradigm (speed, no aliasing).


1 In a job interview in 1998 or so as C++ developer at Star Division, which was developing StarOffice, the original precursor to OpenOffice and LibreOffice, I was asked: "You have a base class, directly or indirectly used throughout the project. Now you would like to add a virtual function to it but avoid recompilation of the whole project, because it would just take too long. Can you do that? How?" The answer is that most implementations probably maintain the virtual functions in a vtable to which you can append without changing the offsets of existing functions (and, of course, without altering the object layout). Obviously, there is no guarantee that the implementation does not generate the vtable backwards, or employs some other mechanism, but in practice that's what you can do.
Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
  • thanks , you really explained it well ,.It has been a while , since I learned about pure virtual functions and they weren't so clear to me, your answer helped a lot. – hanie Jun 05 '20 at 12:59