473,750 Members | 4,639 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Encapsulation and Operator[]

I often see operator[] implemented something like this:

class Foo { ... };

class FooList
{
public:
const Foo& operator[] (unsigned index) const {return
array[index];};
Foo& operator[] (unsigned index) {return
array[index];};
private:
Foo array[num];
};

And this seems natural and intuitive (at least to me). But it seems to
wreck encapsulation. Is there some standard way to avoid this
transgression and still provide the client with an interface that is
natural and easy to use? Or do you just bite the bullet and accept it?
Please pitch responses to someone whose level of knowledge is about
one year of C++ experience.

Thank you,

Roger



Mar 18 '06 #1
47 3360
On Fri, 17 Mar 2006 21:57:20 -0800, "Roger Lakner"
<rl*****@adelph ia.net> wrote:
I often see operator[] implemented something like this:

class Foo { ... };

class FooList
{
public:
const Foo& operator[] (unsigned index) const {return
array[index];};
Foo& operator[] (unsigned index) {return
array[index];};
private:
Foo array[num];
};

And this seems natural and intuitive (at least to me). But it seems to
wreck encapsulation. Is there some standard way to avoid this
transgressio n and still provide the client with an interface that is
natural and easy to use? Or do you just bite the bullet and accept it?
Please pitch responses to someone whose level of knowledge is about
one year of C++ experience.


Why do you say that it wrecks encapsulation? Admittedly, the
implementation shown above doesn't buy you anything over direct public
access to the array member variable, but that doesn't mean that it
can't be done differently.

For example, if index is out of range, an exception can be thrown. The
implementation can also be very complex. Consider that there might not
even be a member "array", but operator[] does a database lookup
instead (somehow). Or that the real array is held in another class,
and FooList holds a pointer or reference to that class to which it
forwards the call. There are many possibilities here.

--
Bob Hairgrove
No**********@Ho me.com
Mar 18 '06 #2

"Roger Lakner" <rl*****@adelph ia.net> skrev i meddelandet
news:Tq******** ************@ad elphia.com...
I often see operator[] implemented something like this:

class Foo { ... };

class FooList
{
public:
const Foo& operator[] (unsigned index) const {return
array[index];};
Foo& operator[] (unsigned index) {return
array[index];};
private:
Foo array[num];
};

And this seems natural and intuitive (at least to me). But it seems
to
wreck encapsulation. Is there some standard way to avoid this
transgression and still provide the client with an interface that is
natural and easy to use?
That is an important feature for an interface!
Or do you just bite the bullet and accept it?
It is not about accepting or not, it is about what you want to do to
an object. Without knowing what a Foo is, it is hard to tell if

FooList f;

f[5] = someFoo;

is "natural" or not. It depends!

Considering that C++ provides a std::vector with this kind of
interface, it must be correct some of the time. :-)

Please pitch responses to someone whose level of knowledge is about
one year of C++ experience.


There aren't always universal rules for all situations. You have to
consider each one individually.
Bo Persson
Mar 18 '06 #3

Roger Lakner wrote:
I often see operator[] implemented something like this:

class Foo { ... };

class FooList
{
public:
const Foo& operator[] (unsigned index) const {return
array[index];};
Foo& operator[] (unsigned index) {return
array[index];};
private:
Foo array[num];
};

And this seems natural and intuitive (at least to me). But it seems to
wreck encapsulation. Is there some standard way to avoid this
transgression and still provide the client with an interface that is
natural and easy to use? Or do you just bite the bullet and accept it?


"Wrecks encapsulation?" On the contrary, FooList demonstrates exactly
how a class should encapsulate its data - with private data members and
a public interface. Note that clients cannot access FooList's data
member directly, instead they must invoke methods in FooList's public
interface to access FooList's data. In other words, FooList has
encapsulated its data by mediating all access to it.

Encapsulation makes it possible for FooList to change its underlying
storage model without affecting its clients - and that quality is the
primary benefit of encapsulation. For instance we could imagine an
implementation in which FooList accessed a network server, or a
database, or some other kind of store to retrieve the Foo object
returned by operator[]. To the client, this change to FooList would go
undetected - because even though its data representation may have
changed - its public interface, which its clients all use - would not
have changed.

Greg

Mar 18 '06 #4
In article <Tq************ ********@adelph ia.com>,
"Roger Lakner" <rl*****@adelph ia.net> wrote:
I often see operator[] implemented something like this:

class Foo { ... };

class FooList
{
public:
const Foo& operator[] (unsigned index) const {return
array[index];};
Foo& operator[] (unsigned index) {return
array[index];};
private:
Foo array[num];
};

And this seems natural and intuitive (at least to me). But it seems to
wreck encapsulation.
Returning a reference/pointer to a member object breaks both the LSP and
UAP (and thus "wrecks encapsulation". ) You are entirely correct on that
point. However there are times when both can be broken... Take
std::vector for example, the vector does not logically own the objects
it contains, it is simply in charge of deleting said objects at the
appropriate time. The object that owns the vector is the one who
actually owns the objects contained in the vector. In other words:

class Foo {
vector<Bar> itsBars;
};

Logically, Foo objects own the Bar objects in the vector, *not* the
vector. If you were to write the above in UML it would look like:

0..n
[Foo]<#>--------->[Bar]

Note how the vector<Bar> is not expressed in the diagram, it is simply
an implementation artifact.

Also, although returning a const reference does break the OO principles
above, as long as it is only used as a performance optimization over
returning an object, it's OK to do. In other words, changing "const T&"
to "T" should not break client code, only slow down the function call.
Any client code that *does* break as a result of such change should be
modified.

In summary, by returning a Foo& in FooList::op[], you (as the designer
of FooList) are saying that FooList doesn't actually own the Foos it
contains, it is simply managing their lifetime for some client who
*does* own them.

Is there some standard way to avoid this
transgression and still provide the client with an interface that is
natural and easy to use? Or do you just bite the bullet and accept it?


First you have to ask yourself, "who owns the Foos that FooList
contains?" If the answer is "FooList" then you should not provide an
op[] for the contained objects (although if you want, you can provide an
op[] const.) Use something like this instead:

class FooList {
Foo array[num];
public:
const Foo operator[](unsigned index) const { return array[index]; }
void setFoo( unsigned id, const Foo& foo ) {
array[id] = foo;
}
};

(If Foo's are expensive to copy and you find that performance is
suffering because of the return by const value, you can later change the
code to "const Foo& operator[](unsigned)const ".)

This way, encapsulation is preserved. You can replace 'array' with any
other class, set of classes, or remove it completely as your needs
dictate without affecting FooList clients.

--
Magic depends on tradition and belief. It does not welcome observation,
nor does it profit by experiment. On the other hand, science is based
on experience; it is open to correction by observation and experiment.
Mar 18 '06 #5
In article <fn************ *************** *****@4ax.com>,
Bob Hairgrove <in*****@bigfoo t.com> wrote:
On Fri, 17 Mar 2006 21:57:20 -0800, "Roger Lakner"
<rl*****@adelph ia.net> wrote:
I often see operator[] implemented something like this:

class Foo { ... };

class FooList
{
public:
const Foo& operator[] (unsigned index) const {return
array[index];};
Foo& operator[] (unsigned index) {return
array[index];};
private:
Foo array[num];
};

And this seems natural and intuitive (at least to me). But it seems to
wreck encapsulation. Is there some standard way to avoid this
transgressio n and still provide the client with an interface that is
natural and easy to use? Or do you just bite the bullet and accept it?
Please pitch responses to someone whose level of knowledge is about
one year of C++ experience.


Why do you say that it wrecks encapsulation? Admittedly, the
implementation shown above doesn't buy you anything over direct public
access to the array member variable, but that doesn't mean that it
can't be done differently.

For example, if index is out of range, an exception can be thrown. The
implementation can also be very complex. Consider that there might not
even be a member "array", but operator[] does a database lookup
instead (somehow). Or that the real array is held in another class,
and FooList holds a pointer or reference to that class to which it
forwards the call. There are many possibilities here.


The above is not quite true. The Foos in FooList *must* be objects in
RAM because clients of FooList may keep a pointer/reference to the value
returned, or modify the state of a FooList object by modifying the Foo
returned. That breaks the UAP (clients know that the return value was
not computed, but rather stored,) thus encapsulation is broken.
--
Magic depends on tradition and belief. It does not welcome observation,
nor does it profit by experiment. On the other hand, science is based
on experience; it is open to correction by observation and experiment.
Mar 18 '06 #6
Bob,
I see your point about the fact that more can go on in the op[]
implementation than just passing a reference. And in that context, it
makes some sense, though I still think encapsulation is violated. But
I have seen several contexts in which nothing goes on except as I've
illustrated (e.g., in std::vector), and it is in this context that it
seems to me just a conceit to make the array private.

Roger

"Bob Hairgrove" <in*****@bigfoo t.com> wrote in message
news:fn******** *************** *********@4ax.c om...
On Fri, 17 Mar 2006 21:57:20 -0800, "Roger Lakner"
<rl*****@adelph ia.net> wrote:
I often see operator[] implemented something like this:

class Foo { ... };

class FooList
{
public:
const Foo& operator[] (unsigned index) const {return
array[index];};
Foo& operator[] (unsigned index) {return
array[index];};
private:
Foo array[num];
};

And this seems natural and intuitive (at least to me). But it seems
to
wreck encapsulation. Is there some standard way to avoid this
transgressi on and still provide the client with an interface that is
natural and easy to use? Or do you just bite the bullet and accept
it?
Please pitch responses to someone whose level of knowledge is about
one year of C++ experience.


Why do you say that it wrecks encapsulation? Admittedly, the
implementation shown above doesn't buy you anything over direct
public
access to the array member variable, but that doesn't mean that it
can't be done differently.

For example, if index is out of range, an exception can be thrown.
The
implementation can also be very complex. Consider that there might
not
even be a member "array", but operator[] does a database lookup
instead (somehow). Or that the real array is held in another class,
and FooList holds a pointer or reference to that class to which it
forwards the call. There are many possibilities here.

--
Bob Hairgrove
No**********@Ho me.com

Mar 19 '06 #7

"Bo Persson" <bo*@gmb.dk> wrote in message
news:48******** ****@individual .net...

There aren't always universal rules for all situations. You have to
consider each one individually.


Amen to that. But if one of the primary advantages of C++ over C is
encapsulation, and that feature is routinely and easily subverted,
then...

Roger
Mar 19 '06 #8

"Greg" <gr****@pacbell .net> wrote in message
news:11******** **************@ i40g2000cwc.goo glegroups.com.. .
On the contrary, FooList demonstrates exactly
how a class should encapsulate its data - with private data members
and
a public interface. Note that clients cannot access FooList's data
member directly, instead they must invoke methods in FooList's
public
interface to access FooList's data. In other words, FooList has
encapsulated its data by mediating all access to it.
I guess I don't see the difference between op[] as instantiated here
and making array public.

Encapsulation makes it possible for FooList to change its underlying
storage model without affecting its clients - and that quality is
the
primary benefit of encapsulation.


I agree that is one of the primary benefits of encapsulation, but so
is data hiding. The data is not being hidden in any robust sense here.
I don't see the difference, in my example, between op[] and making
array public. It's disheartening to me to see that one of the
much-vaunted advantages of C++ over C is so easily, so naturally, so
intuitively and quite often subverted.

Roger
Mar 19 '06 #9

"Daniel T." <po********@ear thlink.net> wrote in message
news:postmaster-
Returning a reference/pointer to a member object breaks both the LSP
and
UAP (and thus "wrecks encapsulation". ) You are entirely correct on
that
point. However there are times when both can be broken...
What sense of "can" do you mean in "can be broken"? I know it is
possible to break both. Do you mean "should" be broken? Should be
broken only in rare cases? Should be broken when other considerations
warrant?
Take std::vector for example, the vector does not logically own the
objects
it contains, it is simply in charge of deleting said objects at the
appropriate time. The object that owns the vector is the one who
actually owns the objects contained in the vector. In other words:

class Foo {
vector<Bar> itsBars;
};

Logically, Foo objects own the Bar objects in the vector, *not* the
vector. If you were to write the above in UML it would look like:

0..n
[Foo]<#>--------->[Bar]

Note how the vector<Bar> is not expressed in the diagram, it is
simply
an implementation artifact.

Also, although returning a const reference does break the OO
principles
above, as long as it is only used as a performance optimization over
returning an object, it's OK to do. In other words, changing "const
T&"
to "T" should not break client code, only slow down the function
call.
Any client code that *does* break as a result of such change should
be
modified.
So performance is what makes it OK to break a primary feature of OO
principles? I agree its advantageous to the programmer, but at what
cost?

In summary, by returning a Foo& in FooList::op[], you (as the
designer
of FooList) are saying that FooList doesn't actually own the Foos it
contains, it is simply managing their lifetime for some client who
*does* own them.
This is a very interesting way of looking at this. I need to give this
some more thought.

Is there some standard way to avoid this
transgression and still provide the client with an interface that
is
natural and easy to use? Or do you just bite the bullet and accept
it?


First you have to ask yourself, "who owns the Foos that FooList
contains?" If the answer is "FooList" then you should not provide an
op[] for the contained objects (although if you want, you can
provide an
op[] const.) Use something like this instead:

class FooList {
Foo array[num];
public:
const Foo operator[](unsigned index) const { return
array[index]; }
void setFoo( unsigned id, const Foo& foo ) {
array[id] = foo;
}
};

(If Foo's are expensive to copy and you find that performance is
suffering because of the return by const value, you can later change
the
code to "const Foo& operator[](unsigned)const ".)

This way, encapsulation is preserved. You can replace 'array' with
any
other class, set of classes, or remove it completely as your needs
dictate without affecting FooList clients.


Thank you very much, this is very helpful.

Roger
Mar 19 '06 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

5
1681
by: jmsantoss | last post by:
Hi, This is a design question. I have a class named "DataBuffer" that stores some data. After "DataBuffer" is created it can not be modified. All the methods of "DataBuffer" are const as data can not be modified after it was created. Up to here everything is fine. The problem is when I want to get clever with data storage. My program has an array of "DataBuffers" that gets pre-allocated. If I want to use that memory instead of...
2
7640
by: subramanian100in | last post by:
Is my following understanding correct ? Data abstraction means providing the interface - that is, the set of functions that can be called by the user of a class. Information hiding means mentioning the class members(functions, typedefs, data) under the access control labels : public, protected, private. Encapsulation means providing the implementation of class member
0
9004
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
8841
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
9587
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
9401
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
0
6086
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4896
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
3328
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
2812
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
2229
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.