C++ structures and classes

C++ structures and classes

The C++ programming language allows programmers to define program-specific datatypes through the use of structures and classes. Instances of these datatypes are known as objects and can contain member variables, constants, member functions, and overloaded operators defined by the programmer. Syntactically, structures and classes are extensions of the C struct datatype, which cannot contain functions or overloaded operators.

Differences between structures and classes

Structures and classes are syntactically identical, except in the default accessibility of their members and their inheritance. By default, members of structures have public accessibility and public inheritance from their parent(s), while members of classes are private and inherit privately from their parent(s). Individual members' accessibility can be specified by using the public, protected and private keywords.

Following the conventions of object-oriented programming, "class" is the more commonly used term for referring to these object datatypes in C++. The semantics of classes, by making members private by default, encourages encapsulation.

tructs in C and C++

As C++ structures are declared by the struct keyword, many programmers refer to C++ structures as "structs" in speech and text, as it is a short form of the word "structure". The C++ struct is an extension of its C counterpart. All C structs, except those using reserved C++ keywords as identifiers, are compatible with the C++ standard and can therefore be compiled as C++ code.

Declaration and usage

C++ structures and classes have their own members. These members include variables (including other structures and classes), functions (specific identifiers or overloaded operators) known as methods, constructors and destructors. Members are declared to be either publicly or privately accessible using the public: and private: access specifiers respectively. Any member encountered after a specifier will have the associated access until another specifier is encountered. There is also inheritance between classes.

Basic declaration and member variables

Classes and structures are declared with the class and struct keywords respectively. Declaration of members are placed within this declaration.

The following code snippets show an example of both a struct and a class declaration:

struct person{ string name; int age;};class person{public: string name; int age;};

Both of these declarations are functionally equivalent. In the above examples name and age are called member variables of the person datatype. Note that the semicolons after the closing curly braces are mandatory.

After one of these declarations (but not both), person can be used as follows to create newly defined variables of the person datatype:


#include
#include using namespace std;

class person{public: string name; int age;};

int main (){ person a, b; a.name = "Calvin"; b.name = "Hobbes"; a.age = 30; b.age = 20; cout << a.name << ": " << a.age << endl; cout << b.name << ": " << b.age << endl; return 0;}

Executing the above code will output Calvin: 30 Hobbes: 20

Member functions

An important feature of the C++ class and structure are member functions. Each datatype can have its own built-in functions (referred to as methods) that have access to all (public and private) members of the datatype. These functions take an implicit first argument (this), which is a pointer to the object that the method was called on. Take the above person type as an example again:

class person{ std::string name; int age;public: person() : age(5) { }; void print();};

void person::print(){ cout << name << ";" << this->age << endl; /* we don't have to mention what "name" and "age" are, because it automatically refers back to the member variables. The "this" variable is the implied first argument, and is a pointer to the object for which the member was invoked. */}

In the above example the print() function is declared in the body of the class and defined by qualifying it with the name of the class followed by ::. Both name and age are private (default for class) and print() is declared as public which is necessary if it is to be used from outside the class.

With the member function print(), printing can be simplified into:a.print();b.print();

where a and b above are called senders, and each of them will refer to their own member variables when the print() function is executed.

It is common practice to separate the class or structure declaration (called its interface) and the definition (called its implementation) into separate units. The interface, needed by the user, is kept in a header and the implementation is kept separately in either source or compiled form.

Methods, just like normal functions, can also be declared to be inline by qualifying the definition with the inline keyword. Additionally since methods are commonly used to only set or return members they can be made inline by defining the methods in the body of the class or structure. For example:class person{ int age;public: int GetAge() { return age; } void SetAge(int i) { age = i; ;

Inheritance

Inheritance is implemented by concatenating classes in memory. This makes the is-a inheritance relationship very natural. For example, considerclass P { int x;};

class C : public P { int y;};An instance of P with a P* p pointing to it would look like this in memory: +----+
P::x
+----+ ↑ pAn instance of C with a C* c pointing to it would look like this: +----+----+
P::x|C::y
+----+----+ ↑ cThus, in a very real sense, *c "is" a P.

Multiple inheritance is not as simple. If a class D inherits P1 and P2, in memory it will be a P1 followed by a P2 followed by the body of the D. When the D needs to be converted to a P2, the compiler will automatically provide a pointer to the P2.

For more on multiple inheritance, see virtual inheritance.

Overloaded operators

In C++, operators, such as + - * /, can be overloaded to suit the needs of programmers. These operators are called overloadable operators.

By convention, overloaded operators should behave nearly the same as they do in built-in datatypes (int, float, etc.), but this is not required. One can declare a structure called integer in which the variable "really" stores an integer, but by calling integer * integer the sum, instead of the product, of the integers might be returned:

struct integer { int i; integer (int j = 0) : i (j) {}; integer operator* (const integer &k) const { return integer (i + k.i); ;

The code above made use of a constructor to "construct" the return value. For clearer presentation (although this will decrease efficiency of the program), the above code can be rewritten as:

integer operator* (const integer &k) const { integer m; m.i = i + k.i; return m;}

Programmers can also put a prototype of the operator in the struct declaration and define the function of the operator in the global scope:

struct integer { int i; integer (int j = 0) : i (j) {}; integer operator* (const integer &k) const;}; integer integer::operator* (const integer &k) const { return integer (i + k.i);}

i above represents the sender's own member variable, while k.i represents the member variable from the argument variable k.

The const keyword appears twice in the above code. The first occurrence, the argument const integer &k, indicated that the argument variable will not be changed by the function. The second incidence at the end of the declaration promises the compiler that the sender would not be changed by the function run.

In const integer &k, the ampersand (&) means "pass by reference". When the function is called, a pointer to the variable, instead of the variable itself, will be passed into the function.

The same overloading properties above apply also to classes.

Note that arity, associativity and precedence of operators cannot be changed.

Binary overloadable operators

Binary operators (operators with two arguments) are overloaded by declaring a function with an "identifier" "operator (something)" which calls one single argument. The variable on the left of the operator is the sender while that on the right is the argument.

integer i = 1; /* we can initialize a structure variable this way as if calling a constructor with only the first argument specified. */integer j = 3;/* variable names may conflict with the names of the member variables of the structure. */integer k = i * j;cout << k.i << endl;

'4' would be printed.

The following is a list of binary overloadable operators:

The '=' (assignment) operator between two variables of the same structure type is overloaded by default to copy the entire content of the variables from one to another. It can be overwritten with something else, if necessary.

Operators must be overloaded one by one, in other words, no overloading is associated with one another. For example, &lt; is not necessarily the opposite of &gt;.

Unary overloadable operators

While some operators, as specified above, takes two terms, sender on the left and the argument on the right, some operators have only one argument - the sender, and they are said to be "unary". Examples are the negative sign (when nothing is put on the left of it) and the "logical NOT" (exclamation mark, !).

Sender of unary operators may be on the left or on the right of the operator. The following is a list of unary overloadable operators:

The syntax of an overloading of a unary operator, where the sender is on the right, is as follows::return_type operator@ ()

When the sender is on the left, the declaration is::return_type operator@ (int)

@ above stands for the operator to be overloaded. Replace return_type with the datatype of the return value (int, bool, structures etc.)

The int parameter essentially means nothing but a convention to show that the sender is on the left of the operator.

const arguments can be added to the end of the declaration if applicable.

Overloading brackets

The square bracket [] and the round bracket () can be overloaded in C++ structures. The square bracket must contain exactly one argument, while the round bracket can contain any specific number of arguments, or no arguments.

The following declaration overloads the square bracket.:return_type operator [] (argument)

The content inside the bracket is specified in the argument part.

Round bracket is overloaded a similar way.:return_type operator() (arg1, arg2, ...)

Contents of the bracket in the operator call are specified in the second bracket.

In addition to the operators specified above, the arrow operator (-&gt;), the starred arrow (-&gt;*), the new keyword and the delete keyword can also be overloaded. These memory-or-pointer-related operators must process memory-allocating functions after overloading. Like the assignment (=) operator, they are also overloaded by default if no specific declaration is made.

Constructors

Sometimes software engineers may want their variables to take a default or specific value upon declaration. This can be done by declaring constructors.

person (string N, int A) { name = N; age = A;}

Member variables can be initialized in an initializer list, with utilization of a colon, as in the example below. This differs from the above in that it initializes (using the constructor), rather than using the assignment operator. This is more efficient for class types, since it just needs to be constructed directly; whereas with assignment, they must be first initialized using the default constructor, and then assigned a different value. Also some types (like references and const types) cannot be assigned to and therefore must be initialized in the initializer list.

person (std::string N, int A) : name (N), age (A) {}

Note that the curly braces cannot be omitted, even if empty.

Default values can be given to the last arguments to help initializing default values.

person (std::string N = "", int A = 0) : name (N), age (A) {}

When no arguments are given to the constructor in the example above, it is equivalent to calling the following constructor with no arguments (a default constructor):

person () : name (""), age (0) {}

The declaration of a constructor looks like a function with the name as the same as the datatype. In fact, we can really call the constructor in form of a function call. In that case a person type variable would be the return value:

int main () { person r = person ("Wales", 40); r.print ();}

The above code creates a temporary person object, and then assigns it to r using the copy constructor. A better way of creating the object (without unnecessary copying) is:

int main () { person r ("Wales", 40); r.print ();}

Specific program actions, which may or may not relate to the variable, can be added as part of the constructor.

person () { std::cout << "Hello!" << endl;}

With the above constructor, a "Hello!" will be printed in case a person variable with no specific value is initialized.

Destructors

A destructor is the reverse of a constructor. It is called when an instance of a class is destroyed, e.g. when an object of a class created in a block (set of curly braces "{}") is deleted after the closing brace, then the destructor is called automatically. It will be called upon emptying of the memory location storing the variable. Destructors can be used to release resources, such as heap-allocated memory and opened files when an instance of that class is destroyed.

The syntax for declaring a destructor is similar to that of a constructor. There is no return value and the name of the method is the same as the name of the class with a tilde (~) in front.

~person () { cout << "I'm deleting " << name << "with age" << age << endl;}

Template structures

C++ allows structures to be declared as templates. In other words, constituent members of a structure do not necessarily come from a specific type. For example, the following structure stores two variables of a user-specific type and has an overloaded plus operator.

template struct twothings { A one; B two; twothings operator+ (const twothings &arg) const { twothings temp; temp.one = one + arg.one; temp.two = two + arg.two; return temp; ;

After that, in the main function, we can use the twothings “structure template” to store variables of any two datatypes as we want to.

twothings twonumber; twothings<string, int> bigmap; twonumber.one = 16; twonumber.two = 1.667; bigmap.one = "Hallo"; bigmap.two = 19;

All of the above statements are valid because the datatypes match correctly. In addition, as in above, a structure or class datatype string (a library class) is used as the type of the constituent variable. This shows structures themselves can be substituted as templates.

Strictly speaking, a structure template is distinct from a structure. This is exemplified by the fact that twothings foo; will not compile but twothings foo; will—only the latter is a fully-defined type. Similarly, twothings and twothings are different types.

This can be confusing to those new to templates. For example, while a float can be implicitly converted to a double, a std::vector will not be converted implicitly to a std::vector. However, because the constructor of a std::vector takes iterators, the following does work:std::vector foo(10, 1.0); // Ten 1.0s.// std::vector bar = foo; // Won't work.std::vectorbar(foo.begin(), foo.end()); // Works; constructs bar by copying the range [foo.begin(), foo.end()).

The implementation of templates in the early versions of C++ has contributed to the birth of the Standard Template Library.

Properties

The syntax of C++ tries to make every aspect of a structure look like that of the basic datatypes. Therefore, overloaded operators allow structures to be manipulated just like integers and floating-point numbers, arrays of structures can be declared with the square-bracket syntax (some_structure variable_name [size] ), and pointers to structures can be dereferenced in the same way as pointers to built-in datatypes.

Memory consumption

The memory consumption of a structure is at least the sum of the memory sizes of its constituent variables. Take the twonums structure below as an example.

struct twonums { int a; int b;};

The structure consists of two integers. In many current C++ compilers, integers are 32-bit integers by default, so each of the member variables consume four bytes of memory. The entire variable, therefore, consumes at least (or exactly) eight bytes of memory, as follows. +----+----+
a | b
+----+----+

However, the compiler may add padding between the variables or at the end of the structure to ensure proper data alignment for a given computer architecture, often padding variables to be 32-bit aligned. For example, the structure

struct bytes_and_such{ char c; char C; short int s; int i; double d;};

could look like +-+-+--+--+--+----+--------+
c|C|XX|s |XX| i | d
+-+-+--+--+--+----+--------+in memory, where XX is two unused bytes.

As structures may make use of pointers and arrays to declare and initialize its member variables, memory consumption of structures is not necessarily constant. Another example of non-constant memory size is template structures.

Pass by reference

Many programmers prefer to use the ampersand (&) to declare the arguments of a function involving structures. This is because by using the dereferencing ampersand only one word (typically 4 bytes on a 32 bit machine, 8 bytes on a 64 bit machine) is required to be passed into the function, namely the memory location to the variable. Otherwise, if pass-by-value is used, the argument needs to be copied every time the function is called, which is costly with large structures.

Since pass-by-reference exposes the original structure to be modified by the function, the const keyword should be used to guarantee that the function does not modify the parameter (see const-correctness), when this is not intended.

The "this" keyword

To facilitate efficient use of structures, C++ implements the this keyword for function calls, constructors and destructors of a structure to refer to its own location.

Datatype of this is a pointer to structure.

The this keyword is especially important for member functions with the structure itself as the return value:

complex operator+= (const complex & c) { realPart += c.realPart; imagPart += c.imagPart; return *this;}

As stated above, this is a pointer, so we have to use the asterisk (*) or pointer-to-member (->) dereferencing operator to retrieve the value of the structure for calculation.

Note that the const keyword appears only once in the first line. This is because the sender is changed by the function, while the parameter is not.

When the sender variable is changed and then returned by the function call, the ampersand may also be used in the return type declaration:

complex& operator += (const complex & c)

ee also

*Object composition
*Virtual inheritance

References

* [http://www.conlinetest.com C Online Quiz] C Online Quiz- Test Your C Skills.
# [http://www.cplusplus.com/doc/tutorial/classes2.html Cplusplus.com tutorial lesson 5.2] , accessed in January 2006
#Chan, S. "Tutorial on C++ and STL", Hong Kong, 2006
# [http://www.cplusplus.com/doc/tutorial/basic_io.html Cplusplus.com tutorial lesson 2.5] , accessed in February 2006


Wikimedia Foundation. 2010.

Игры ⚽ Поможем сделать НИР

Look at other dictionaries:

  • Semiotic elements and classes of signs — C. S. Peirce articles  General:    Charles Sanders Peirce Charles Sanders Peirce bibliography Philosophical:    Categories (Peirce) Semiotic elements and   classes of signs (Peirce) Pragmatic maxim • Pragmaticism… …   Wikipedia

  • C++ classes — For background information, see C++. The C++ programming language allows programmers to separate program specific datatypes through the use of classes. Instances of these datatypes are known as objects and can contain member variables, constants …   Wikipedia

  • Classes de complexité P et NP — Théorie de la complexité des algorithmes La théorie de la complexité des algorithmes étudie formellement la difficulté intrinsèque des problèmes algorithmiques. Sommaire 1 Histoire 2 Généralités 2.1 Présentation …   Wikipédia en Français

  • Outline of algebraic structures — In universal algebra, a branch of pure mathematics, an algebraic structure is a variety or quasivariety. Abstract algebra is primarily the study of algebraic structures and their properties. Some axiomatic formal systems that are neither… …   Wikipedia

  • List of algebraic structures — In universal algebra, a branch of pure mathematics, an algebraic structure is a variety or quasivariety. Abstract algebra is primarily the study of algebraic structures and their properties. Some axiomatic formal systems that are neither… …   Wikipedia

  • Mediation (Marxist theory and media studies) — Karl Marx Born May 5, 1818(1818 05 05) Trier, Prussia Died March 14, 1883(1883 03 14) (aged  …   Wikipedia

  • Discovery and development of small molecule c-Met inhibitors — The c Met (mesenchymal epithelial transition) tyrosine kinase stimulates cell scattering, invasion, protection from apoptosis and angiogenesis.[1] c Met is a receptor tyrosine kinase,[2] which can cause a wide variety of different cancers, such… …   Wikipedia

  • garden and landscape design — Introduction       the development and decorative planting of gardens, yards, grounds, parks, and other types of areas. Gardening and landscape design is used to enhance the settings for buildings and public areas and in recreational areas and… …   Universalium

  • List of tallest structures in the world — Map of all coordinates from Google Map of all coordinates from Bing Export all coordinates as KML …   Wikipedia

  • State and Local Affairs — ▪ 1997 Introduction       States continued to be at the centre of national debates on public policy during 1996. The U.S. Congress, reacting in part to successful experimentation by a number of states, enacted a historic welfare reform measure… …   Universalium

Share the article and excerpts

Direct link
Do a right-click on the link above
and select “Copy Link”