Main Content

Return of non-const handle to encapsulated data member

Method returns pointer or reference to internal member of object

Description

This defect occurs when:

  • A class method returns a handle to a data member. Handles include pointers and references.

  • The method is more accessible than the data member. For instance, the method has access specifier public, but the data member is private or protected.

Risk

The access specifier determines the accessibility of a class member. For instance, a class member declared with the private access specifier cannot be accessed outside a class. Therefore, nonmember, nonfriend functions cannot modify the member.

When a class method returns a handle to a less accessible data member, the member accessibility changes. For instance, if a public method returns a pointer to a private data member, the data member is effectively not private anymore. A nonmember, nonfriend function calling the public method can use the returned pointer to view and modify the data member.

Also, if you assign the pointer to a data member of an object to another pointer, when you delete the object, the second pointer can be left dangling. The second pointer points to the part of an object that does not exist anymore.

Fix

One possible fix is to avoid returning a handle to a data member from a class method. Return a data member by value so that a copy of the member is returned. Modifying the copy does not change the data member.

If you must return a handle, use a const qualifier with the method return type so that the handle allows viewing, but not modifying, the data member.

Examples

expand all

#include <string>
#define NUM_RECORDS 100

struct Date {
    int dd;
    int mm;
    int yyyy;
};


struct Period {
    Date startDate;
    Date endDate;
};

class DataBaseEntry {
private:
    std::string employeeName;
    Period employmentPeriod;
public:
    Period* getPeriod(void);
};

Period* DataBaseEntry::getPeriod(void) {
    return &employmentPeriod;
}


void use(Period*);
void reset(Period*);

int main() {
    DataBaseEntry dataBase[NUM_RECORDS];
    Period* tempPeriod;
    for(int i=0;i < NUM_RECORDS;i++) {
        tempPeriod = dataBase[i].getPeriod();
        use(tempPeriod);
        reset(tempPeriod);
    }
    return 0;
}

void reset(Period* aPeriod) {
       aPeriod->startDate.dd = 1;
       aPeriod->startDate.mm = 1;
       aPeriod->startDate.yyyy = 2000;
}

In this example, employmentPeriod is private to the class DataBaseEntry. It is therefore immune from modification by nonmember, nonfriend functions. However, returning a pointer to employmentPeriod breaks this encapsulation. For instance, the nonmember function reset modifies the member startDate of employmentPeriod.

Correction: Return Member by Value

One possible correction is to return the data member employmentPeriod by value instead of pointer. Modifying the return value does not change the data member because the return value is a copy of the data member.

#include <string>
#define NUM_RECORDS 100

struct Date {
    int dd;
    int mm;
    int yyyy;
};


struct Period {
    Date startDate;
    Date endDate;
};

class DataBaseEntry {
private:
    std::string employeeName;
    Period employmentPeriod;
public:
    Period getPeriod(void);
};

Period DataBaseEntry::getPeriod(void) {
    return employmentPeriod;
}


void use(Period*);
void reset(Period*);

int main() {
    DataBaseEntry dataBase[NUM_RECORDS];
    Period tempPeriodVal;
    Period* tempPeriod;
    for(int i=0;i < NUM_RECORDS;i++) {
        tempPeriodVal = dataBase[i].getPeriod();
        tempPeriod = &tempPeriodVal;
        use(tempPeriod);
        reset(tempPeriod);
    }
    return 0;
}

void reset(Period* aPeriod) {
       aPeriod->startDate.dd = 1;
       aPeriod->startDate.mm = 1;
       aPeriod->startDate.yyyy = 2000;
}

Result Information

Group: Object oriented
Language: C++
Default: Off
Command-Line Syntax: BREAKING_DATA_ENCAPSULATION
Impact: Medium

Version History

Introduced in R2015b