简体   繁体   中英

C++ - Change private member from outside the class

Is this code causes undefined behavior? Or can i run into a problem with this? (copy the full class without functions, just variables with public modifier and modify the private memebers throw this pointer) example:

#include <iostream>

using namespace std;

class Point {

private:

    int x;
    int y;

public:

    Point(int x, int y) {
        this->x = x;
        this->y = y;
    }

    void Print() {
        cout << "(" << x << ", " << y << ")" << endl;
    }

};

struct PointHack {

    int x;
    int y;

};

int main() {
    Point a(4, 5);
    a.Print();
    ((PointHack *) (&a))->x = 1;
    ((PointHack *) (&a))->y = 2;
    a.Print();

    return 0;
}

output:

(4, 5)
(1, 2)

(with the original member order, of course)

Despite your classes being layout compatible (see below), your code exhibits undefined behavior due to the fact that such pointer casts are prohibited by the C++ strict aliasing rules 1 .

But: replacing the casts with a union makes the code standard-compliant; this is actually guaranteed to work in C++11:

#include <iostream>

using namespace std;

class Point {

private:

    int x;
    int y;

public:

    Point(int x, int y) {
        this->x = x;
        this->y = y;
    }

    void Print() {
        cout << "(" << x << ", " << y << ")" << endl;
    }

};

struct PointHack {
    int x;
    int y;
};

union pu
{
    Point p;
    PointHack ph;
    pu(int x, int y) : p(x, y) {}
};

int main() {
    pu u(4,5);
    u.p.Print();
    u.ph.x=1;
    u.ph.y=2;
    u.p.Print();
    return 0;
}

This comes from the fact that Point and PointHack are standard-layout classes 2 (C++11, §9 ¶7), and share a "common initial subsequence" (§9.2, ¶20); as such, if they both are stored in the same union (here pu ) it's permitted to "inspect the common initial part of any of them" 3 .

Still, this answer is mostly an exercise of style; don't exploit such tricks unless you are really forced to. C++ provides better means to access private members if necessary without brutally breaking the encapsulation - you have getters/setters, protected inheritance, friend classes, ... And in general, if you access private class members in ways not intended by your target class, you are potentially violating the assumptions of that class about how its data is modified, which can lead to erratic behavior of the code of its methods.


Notes:

  1. In C++ you can't have two pointers of unrelated types pointing to the same object; this restriction is mostly used to help optimizers with assumptions about aliasing.
  2. Notice that the requirements for this are quite stringent; typically most classes that aren't basically C structs don't qualify for this. Even having different access qualifiers can break the magic.
  3. The idea is that assigning to ph makes it the "active object" of the union , and then p.Print() is the one "inspecting" the "inactive" object.

Yes, the posted code invokes undefined behavior. Don't do that.

If you want to see a really insane way that some people accomplish this unsavory goal, here you go: access private member using template trick

But really, don't do that.

PS: never use C-style casts in C++. Use static_cast, dynamic_cast, reinterpret_cast, and const_cast, as appropriate. C-style casts are needlessly unsafe.

i found the problem

/*code:*/
#include <stdio.h>

void check(short *h,long *k)
{
    *h=5;
    *k=6;
    if (*h == 5)
        printf("strict aliasing problem\n");
}

int main(void)
{
    long      k[1];
    check((short *)k,k);
    return 0;
}
/*
$ gcc -Wall -ansi -pedantic -O0 -o o0 asd.c
$ ./o0
/output: nothing/
$ gcc -Wall -ansi -pedantic -O2 -o o2 asd.c
$ ./o2
/output: strict aliasing problem/
*/

(same as in c++)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM