Upload
mireya-pepen
View
86
Download
0
Tags:
Embed Size (px)
Citation preview
1. Структура программы main, params, #define
// my second program in C++
#include <iostream>
using namespace std;
int main ()
{
cout << "Hello World! ";
cout << "I'm a C++ program";
}
Line 2: #include <iostream>
Lines beginning with a hash sign (#) are directives read and interpreted by what is known
as the preprocessor. They are special lines interpreted before the compilation of the
program itself begins. In this case, the directive#include <iostream>, instructs the
preprocessor to include a section of standard C++ code, known as header iostream, that allows to perform standard input and output operations, such as writing the output of this
program (Hello World) to the screen.
Line 4: int main ()
This line initiates the declaration of a function. Essentially, a function is a group of code statements which are given a name: in this case, this gives the name "main" to the group of code statements that follow. Functions will be discussed in detail in a later chapter, but essentially, their definition is introduced with a succession of a type (int), a name (main)
and a pair of parentheses (()), optionally including parameters.
The function named main is a special function in all C++ programs; it is the function called
when the program is run. The execution of all C++ programs begins with the main function, regardless of where the function is actually located within the code.
Line 6: std::cout << "Hello World!";
This line is a C++ statement. A statement is an expression that can actually produce some effect. It is the meat of a program, specifying its actual behavior. Statements are executed in the same order that they appear within a function's body.
This statement has three parts: First, std::cout, which identifies
the standard character output device (usually, this is the computer screen). Second, the
insertion operator (<<), which indicates that what follows is inserted intostd::cout.
Finally, a sentence within quotes ("Hello world!"), is the content inserted into the standard output.
Notice that the statement ends with a semicolon (;). This character marks the end of the
statement, just as the period ends a sentence in English. All C++ statements must end with a semicolon character. One of the most common syntax errors in C++ is forgetting to end a statement with a semicolon.
If you have seen C++ code before, you may have seen cout being used instead of std::cout.
Both name the same object: the first one uses its unqualified name (cout), while the second
qualifies it directly within the namespace std (asstd::cout).
cout is part of the standard library, and all the elements in the standard C++ library are declared
within what is a called anamespace: the namespace std.
In order to refer to the elements in the std namespace a program shall either qualify each and
every use of elements of the library (as we have done by prefixing cout with std::), or introduce
visibility of its components. The most typical way to introduce visibility of these components is by means of using declarations:
using namespace std;
The above declaration allows all elements in the std namespace to be accessed in
an unqualified manner (without thestd:: prefix).
-----------------------------------------------------------------------------------------------------
Help you to input Params dynamic input parameters …
By using the params keyword, you can specify a method parameter that takes a variable number of
arguments.
You can send a comma-separated list of arguments of the type specified in the parameter declaration
or an array of arguments of the specified type. You also can send no arguments. If you send no
arguments, the length of the params list is zero.
No additional parameters are permitted after the params keyword in a method declaration, and only
one params keyword is permitted in a method declaration.
public class MyClass
{
public static void UseParams(params int[] list)
{
for (int i = 0; i < list.Length; i++)
{
Console.Write(list[i] + " ");
}
Console.WriteLine();
}
public static void UseParams2(params object[] list)
{
for (int i = 0; i < list.Length; i++)
{
Console.Write(list[i] + " ");
}
Console.WriteLine();
}
static void Main()
{
// You can send a comma-separated list of arguments of the
// specified type.
UseParams(1, 2, 3, 4);
UseParams2(1, 'a', "test");
// A params parameter accepts zero or more arguments.
// The following calling statement displays only a blank line.
UseParams2();
// An array argument can be passed, as long as the array
// type matches the parameter type of the method being called.
int[] myIntArray = { 5, 6, 7, 8, 9 };
UseParams(myIntArray);
object[] myObjArray = { 2, 'b', "test", "again" };
UseParams2(myObjArray);
// The following call causes a compiler error because the object
// array cannot be converted into an integer array.
//UseParams(myObjArray);
// The following call does not cause an error, but the entire
// integer array becomes the first element of the params array.
UseParams2(myIntArray);
}
}
/*
Output:
1 2 3 4
1 a test
5 6 7 8 9
2 b test again
System.Int32[]
*/
----------------------------------------------------------------------------------------------------------------------------- --------------------------------------
The preprocessors are the directives, which give instruction to the compiler to preprocess the information before actual compilation starts.
All preprocessor directives begin with #, and only white-space characters may appear before a preprocessor directive on a line. Preprocessor directives are not C++ statements, so they do not end in a semicolon (;).
You already have seen a #include directive in all the examples. This macro is used to include a header file into the source file.
There are number of preprocessor directives supported by C++ like #include, #define, #if, #else, #line, etc. Let us see important directives:
The #define Preprocessor: The #define preprocessor directive creates symbolic constants. The symbolic constant is called amacro and the general form of the directive is:
#define macro-name replacement-text
When this line appears in a file, all subsequent occurrences of macro in that file will be replaced by replacement-text before the program is compiled. For example:
#include <iostream>
using namespace std;
#define PI 3.14159
int main ()
{
cout << "Value of PI :" << PI << endl;
return 0;
}
Now, let us do the preprocessing of this code to see the result, assume we have source code file, so let us compile it with -E option and redirect the result to test.p. Now, if you will check test.p, it will have lots of information and at the bottom, you will fine the value replaced as follows:
$gcc -E test.cpp > test.p
...
int main ()
{
cout << "Value of PI :" << 3.14159 << endl;
return 0;
}
Function-Like Macros: You can use #define to define a macro which will take argument as follows:
#include <iostream>
using namespace std;
#define MIN(a,b) (((a)<(b)) ? a : b)
int main ()
{
int i, j;
i = 100;
j = 30;
cout <<"The minimum is " << MIN(i, j) << endl;
return 0;
}
If we compile and run above code, this would produce the following result:
The minimum is 30
Conditional Compilation: There are several directives, which can use to compile selectively portions of your program's source code. This process is called conditional compilation.
The conditional preprocessor construct is much like the if selection structure. Consider the following preprocessor code:
#ifndef NULL
#define NULL 0
#endif
You can compile a program for debugging purpose and can debugging turn on or off using a single macro as follows:
#ifdef DEBUG
cerr <<"Variable x = " << x << endl;
#endif
causes the cerr statement to be compiled in the program if the symbolic constant DEBUG has been defined before directive #ifdef DEBUG. You can use #if 0 statment to comment out a portion of the program as follows:
#if 0
code prevented from compiling
#endif
Let us try the following example:
#include <iostream>
using namespace std;
#define DEBUG
#define MIN(a,b) (((a)<(b)) ? a : b)
int main ()
{
int i, j;
i = 100;
j = 30;
#ifdef DEBUG
cerr <<"Trace: Inside main function" << endl;
#endif
#if 0
/* This is commented part */
cout << MKSTR(HELLO C++) << endl;
#endif
cout <<"The minimum is " << MIN(i, j) << endl;
#ifdef DEBUG
cerr <<"Trace: Coming out of main function" << endl;
#endif
return 0;
}
If we compile and run above code, this would produce the following result:
Trace: Inside main function
The minimum is 30
Trace: Coming out of main function
The # and ## Operators: The # and ## preprocessor operators are available in C++ and ANSI/ISO C. The # operator causes a replacement-text token to be converted to a string surrounded by quotes.
Consider the following macro definition:
#include <iostream>
using namespace std;
#define MKSTR( x ) #x
int main ()
{
cout << MKSTR(HELLO C++) << endl;
return 0;
}
If we compile and run above code, this would produce the following result:
HELLO C++
Let us see how it worked. It is simple to understand that the C++ preprocessor turns the line:
cout << MKSTR(HELLO C++) << endl;
into the following line:
cout << "HELLO C++" << endl;
The ## operator is used to concatenate two tokens. Here is an example:
#define CONCAT( x, y ) x ## y
When CONCAT appears in the program, its arguments are concatenated and used to replace the macro. For example, CONCAT(HELLO, C++) is replaced by "HELLO C++" in the program as follows.
#include <iostream>
using namespace std;
#define concat(a, b) a ## b
int main()
{
int xy = 100;
cout << concat(x, y);
return 0;
}
If we compile and run above code, this would produce the following result:
100
Let us see how it worked. It is simple to understand that the C++ preprocessor transforms:
cout << concat(x, y);
into the following line:
cout << xy;
----------------------------------------------------------------------------------------------------------------------------- --------------------------------------
Include guard From Wikipedia, the free encyclopedia
The correct title of this article is #include guard. The substitution or omission of the # is because of technical restrictions.
It has been suggested that pragma once be merged into this article.
(Discuss) Proposed since March 2013.
In the C and C++ programming languages, an #include guard, sometimes called a macro guard, is a particular construct used to avoid the problem of double inclusion when dealing with the include directive. The addition of #include guards to a header file is one way to make that file idempotent.
Contents
[hide]
1 Double inclusion
2 Use of #include guards
3 Difficulties
4 See also
5 External links
6 References
Double inclusion[edit]
The following C code demonstrates a real problem that can arise if #include guards are missing:
File "grandfather.h"
struct foo {
int member;
};
File "father.h"
#include "grandfather.h"
File "child.c"
#include "grandfather.h"
#include "father.h"
Here, the file "child.c" has indirectly included two copies of the text in the header
file "grandfather.h". This causes a compilation error, since the structure type foo is apparently
defined twice. In C++, this would be a violation of the One Definition Rule.
Use of #include guards[edit] File "grandfather.h"
#ifndef GRANDFATHER_H
#define GRANDFATHER_H
struct foo {
int member;
};
#endif /* GRANDFATHER_H */
File "father.h"
#include "grandfather.h"
File "child.c"
#include "grandfather.h"
#include "father.h"
Here, the first inclusion of "grandfather.h" causes the macro GRANDFATHER_H to be defined.
Then, when "child.c" includes "grandfather.h" the second time, the #ifndef test returns false,
and the preprocessor skips down to the #endif , thus avoiding the second definition of struct
foo . The program compiles correctly.
Different naming conventions for the guard macro may be used by different programmers. Other common forms of the above example
includeGRANDFATHER_INCLUDED , CREATORSNAME_YYYYMMDD_HHMMSS (with the appropriate
time information substituted), and names generated from a UUID. (However, names starting with
one or two underscores, such as _GRANDFATHER_H and __GRANDFATHER_H , are reserved to
the implementation and must not be used by the user.[1][2]) It is important to avoid duplicating the name in different header files, as including one will prevent the symbols in the other being defined.
Difficulties[edit]
In order for #include guards to work properly, each guard must test and conditionally set a different preprocessor macro. Therefore, a project using #include guards must work out a coherent naming scheme for its include guards, and make sure its scheme doesn't conflict with that of any third-party headers it uses, or with the names of any globally visible macros.
For this reason, most C and C++ implementations provide a non-standard #pragma
once directive. This directive, inserted at the top of a header file, will ensure that the file is
included only once. The Objective-C language (which is a superset of C) introduced
an #import directive, which works exactly like#include , except that it includes each file only
once, thus obviating the need for #include guards.[3]
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2. Типы данных
Data Types
While doing programming in any programming language, you need to use various variables to store various information. Variables are nothing but reserved memory locations to store values. This means that when you create a variable you reserve some space in memory.
You may like to store information of various data types like character, wide character, integer, floating point, double floating point, boolean etc. Based on the data type of a variable, the operating system allocates memory and decides what can be stored in the reserved memory.
Primitive Built-in Types: C++ offer the programmer a rich assortment of built-in as well as user defined data types. Following table lists down seven basic C++ data types:
Type Keyword
Boolean bool
Character char
Integer int
Floating point float
Double floating point double
Valueless void
Wide character wchar_t
Several of the basic types can be modified using one or more of these type modifiers:
signed
unsigned
short
long
The following table shows the variable type, how much memory it takes to store the value in memory, and what is maximum and minimum vaue which can be stored in such type of variables.
Type Typical Bit Width Typical Range
char 1byte -127 to 127 or 0 to 255
unsigned char 1byte 0 to 255
signed char 1byte -127 to 127
int 4bytes -2147483648 to 2147483647
unsigned int 4bytes 0 to 4294967295
signed int 4bytes -2147483648 to 2147483647
short int 2bytes -32768 to 32767
unsigned short int Range 0 to 65,535
signed short int Range -32768 to 32767
long int 4bytes -2,147,483,647 to 2,147,483,647
signed long int 4bytes same as long int
unsigned long int 4bytes 0 to 4,294,967,295
float 4bytes +/- 3.4e +/- 38 (~7 digits)
double 8bytes +/- 1.7e +/- 308 (~15 digits)
long double 8bytes +/- 1.7e +/- 308 (~15 digits)
wchar_t 2 or 4 bytes 1 wide character
The sizes of variables might be different from those shown in the above table, depending on the compiler and the computer you are using.
Following is the example, which will produce correct size of various data types on your computer.
#include <iostream>
using namespace std;
int main()
{
cout << "Size of char : " << sizeof(char) << endl;
cout << "Size of int : " << sizeof(int) << endl;
cout << "Size of short int : " << sizeof(short int) << endl;
cout << "Size of long int : " << sizeof(long int) << endl;
cout << "Size of float : " << sizeof(float) << endl;
cout << "Size of double : " << sizeof(double) << endl;
cout << "Size of wchar_t : " << sizeof(wchar_t) << endl;
return 0;
}
This example uses endl, which inserts a new-line character after every line and << operator is being used to pass multiple values out to the screen. We are also using sizeof() function to get size of various data types.
When the above code is compiled and executed, it produces the following result which can vary from machine to machine:
Size of char : 1
Size of int : 4
Size of short int : 2
Size of long int : 4
Size of float : 4
Size of double : 8
Size of wchar_t : 4
typedef Declarations: You can create a new name for an existing type using typedef. Following is the simple syntax to define a new type using typedef:
typedef type newname;
For example, the following tells the compiler that feet is another name for int:
typedef int feet;
Now, the following declaration is perfectly legal and creates an integer variable called distance:
feet distance;
Enumerated Types: An enumerated type declares an optional type name and a set of zero or more identifiers that can be used as values of the type. Each enumerator is a constant whose type is the enumeration.
To create an enumeration requires the use of the keyword enum. The general form of an enumeration type is:
enum enum-name { list of names } var-list;
Here, the enum-name is the enumeration's type name. The list of names is comma separated.
For example, the following code defines an enumeration of colors called colors and the variable c of type color. Finally, c is assigned the value "blue".
enum color { red, green, blue } c;
c = blue;
By default, the value of the first name is 0, the second name has the value 1, the third has the value 2, and so on. But you can give a name a specific value by adding an initializer. For example, in the following enumeration, green will have the value 5.
enum color { red, green=5, blue };
Here, blue will have a value of 6 because each name will be one greater than the one that precedes it.
----------------------------------------------------------------------------------------------------------------------------- ----
Stacks in computing architectures are regions of memory where data is added or removed in a last-in-first-out manner. In most modern computer systems, each thread has a reserved region of memory referred to as its stack. When a function executes, it may add some of its state data to the top of the stack; when the function exits it is responsible for removing that data from the stack. At a minimum, a thread's stack is used to store the location of function calls in order to allow return statements to return to the correct location, but programmers may further choose to explicitly use the stack. If a region of memory lies on the thread's stack, that memory is said to have been allocated on the stack.
3. Размещение данных в памяти, стек, куча, указатели
• 3. placement of data in memory, stack, heap, pointers
4. Указатели, взятие адреса, разыменование, косв адресация
• 4. pointers, taking the address, dereferencing, indirect addressing
a pointer is a programming language object, whose value refers to (or "pointsto") another value stored elsewhere in the computer memory using its address. A pointer referencesa location in memory, and obtaining the value stored at that location is known as dereferencing the pointer.
The address of a variable can be obtained by preceding the name of a variable with an
ampersand sign (&), known asaddress-of operator. For example:
foo = &myvar;
This would assign the address of variable myvar to foo; by preceding the name of the
variable myvar with the address-of operator (&), we are no longer assigning the content of the
variable itself to foo, but its address.
The actual address of a variable in memory cannot be known before runtime, but let's assume,
in order to help clarify some concepts, that myvar is placed during runtime in the memory
address 1776.
In this case, consider the following code fragment:
1
2
3
myvar = 25;
foo = &myvar;
bar = myvar;
The values contained in each variable after the execution of this are shown in the following
diagram:
First, we have assigned the value 25 to myvar (a variable whose address in memory we
assumed to be 1776).
The second statement assigns foo the address of myvar, which we have assumed to be 1776.
Finally, the third statement, assigns the value contained in myvar to bar. This is a standard
assignment operation, as already done many times in earlier chapters.
The main difference between the second and third statements is the appearance of
the address-of operator (&).
The variable that stores the address of another variable (like foo in the previous example) is
what in C++ is called apointer. Pointers are a very powerful feature of the language that has
many uses in lower level programming. A bit later, we will see how to declare and use
pointers.
Dereference operator (*)
As just seen, a variable which stores the address of another variable is called a pointer.
Pointers are said to "point to" the variable whose address they store.
An interesting property of pointers is that they can be used to access the variable they point to
directly. This is done by preceding the pointer name with the dereference operator (*). The
operator itself can be read as "value pointed to by".
Therefore, following with the values of the previous example, the following statement:
baz = *foo;
This could be read as: "baz equal to value pointed to by foo", and the statement would
actually assign the value 25 tobaz, since foo is 1776, and the value pointed to
by 1776 (following the example above) would be 25.
It is important to clearly differentiate that foo refers to the value 1776, while *foo (with an
asterisk * preceding the identifier) refers to the value stored at address 1776, which in this
case is 25. Notice the difference of including or not including the dereference operator (I
have added an explanatory comment of how each of these two expressions could be read):
1
2
baz = foo; // baz equal to foo (1776)
baz = *foo; // baz equal to value pointed to by foo (25)
The reference and dereference operators are thus complementary:
& is the address-of operator, and can be read simply as "address of"
* is the dereference operator, and can be read as "value pointed to by"
Indirect address
An address that serves as a reference point instead of the
address to the direct location. For example, if a programmer
saved something to an indirect address in memory, the data
is saved to any free spot in the memory, instead of a specific
address.
5. Операторы и операции
5. Operators and operations
Operators Once introduced to variables and constants, we can begin to operate with them by
using operators. What follows is a complete list of operators. At this point, it is likely not
necessary to know all of them, but they are all listed here to also serve as reference.
Assignment operator (=)
The assignment operator assigns a value to a variable.
x = 5;
This statement assigns the integer value 5 to the variable x. The assignment operation always
takes place from right to left, and never the other way around:
x = y;
This statement assigns to variable x the value contained in variable y. The value of x at the
moment this statement is executed is lost and replaced by the value of y.
Consider also that we are only assigning the value of y to x at the moment of the assignment
operation. Therefore, if ychanges at a later moment, it will not affect the new value taken
by x.
For example, let's have a look at the following code - I have included the evolution of the
content stored in the variables as comments:
1
2
3
4
5
6
7
8
9
10
11
// assignment operator
#include <iostream>
using namespace std;
int main ()
{
int a, b; // a:?, b:?
a = 10; // a:10, b:?
b = 4; // a:10, b:4
a = b; // a:4, b:4
b = 7; // a:4, b:7
a:4 b:7 Edit & Run
12
13
14
15
16
17
cout << "a:";
cout << a;
cout << " b:";
cout << b;
}
This program prints on screen the final values of a and b (4 and 7, respectively). Notice
how a was not affected by the final modification of b, even though we declared a = b earlier.
Assignment operations are expressions that can be evaluated. That means that the assignment
itself has a value, and -for fundamental types- this value is the one assigned in the operation.
For example:
y = 2 + (x = 5);
In this expression, y is assigned the result of adding 2 and the value of another assignment
expression (which has itself a value of 5). It is roughly equivalent to:
1
2
x = 5;
y = 2 + x;
With the final result of assigning 7 to y.
The following expression is also valid in C++:
x = y = z = 5;
It assigns 5 to the all three variables: x, y and z; always from right-to-left.
Arithmetic operators ( +, -, *, /, % )
The five arithmetical operations supported by C++ are:
operator description
+ addition
- subtraction
* multiplication
/ division
% modulo
Operations of addition, subtraction, multiplication and division correspond literally to their
respective mathematical operators. The last one, modulo operator, represented by a
percentage sign (%), gives the remainder of a division of two values. For example:
x = 11 % 3;
results in variable x containing the value 2, since dividing 11 by 3 results in 3, with a
remainder of 2.
Compound assignment (+=, -=, *=, /=, %=, >>=, <<=, &=, ^=, |=)
Compound assignment operators modify the current value of a variable by performing an
operation on it. They are equivalent to assigning the result of an operation to the first
operand:
expression equivalent to...
y += x; y = y + x;
x -= 5; x = x - 5;
x /= y; x = x / y;
price *= units + 1; price = price * (units+1);
and the same for all other compound assignment operators. For example:
1
2
3
4
5
6
7
8
9
10
11
// compound assignment operators
#include <iostream>
using namespace std;
int main ()
{
int a, b=3;
a = b;
a+=2; // equivalent to a=a+2
cout << a;
}
5 Edit & Run
Increment and decrement (++, --)
Some expression can be shortened even more: the increase operator (++) and the decrease
operator (--) increase or reduce by one the value stored in a variable. They are equivalent
to +=1 and to -=1, respectively. Thus:
1
2
3
++x;
x+=1;
x=x+1;
are all equivalent in its functionality; the three of them increase by one the value of x.
In the early C compilers, the three previous expressions may have produced different
executable code depending on which one was used. Nowadays, this type of code optimization
is generally performed automatically by the compiler, thus the three expressions should
produce exactly the same executable code.
A peculiarity of this operator is that it can be used both as a prefix and as a suffix. That
means that it can be written either before the variable name (++x) or after it (x++). Although
in simple expressions like x++ or ++x, both have exactly the same meaning; in other
expressions in which the result of the increment or decrement operation is evaluated, they
may have an important difference in their meaning: In the case that the increase operator is
used as a prefix (++x) of the value, the expression evaluates to the final value of x, once it is
already increased. On the other hand, in case that it is used as a suffix (x++), the value is also
increased, but the expression evaluates to the value that x had before being increased. Notice
the difference:
Example 1 Example 2
x = 3;
y = ++x;
// x contains 4, y contains 4
x = 3;
y = x++;
// x contains 4, y contains 3
In Example 1, the value assigned to y is the value of x after being increased. While
in Example 2, it is the value x had before being increased.
Relational and comparison operators ( ==, !=, >, <, >=, <= )
Two expressions can be compared using relational and equality operators. For example, to
know if two values are equal or if one is greater than the other.
The result of such an operation is either true or false (i.e., a Boolean value).
The relational operators in C++ are:
operator description
== Equal to
!= Not equal to
< Less than
> Greater than
<= Less than or equal to
>= Greater than or equal to
Here there are some examples:
1
2
3
4
5
(7 == 5) // evaluates to false
(5 > 4) // evaluates to true
(3 != 2) // evaluates to true
(6 >= 6) // evaluates to true
(5 < 5) // evaluates to false
Of course, it's not just numeric constants that can be compared, but just any value, including,
of course, variables. Suppose that a=2, b=3 and c=6, then:
1
2
3
4
(a == 5) // evaluates to false, since a is not equal to 5
(a*b >= c) // evaluates to true, since (2*3 >= 6) is true
(b+4 > a*c) // evaluates to false, since (3+4 > 2*6) is false
((b=2) == a) // evaluates to true
Be careful! The assignment operator (operator =, with one equal sign) is not the same as the
equality comparison operator (operator ==, with two equal signs); the first one (=) assigns the
value on the right-hand to the variable on its left, while the other (==) compares whether the
values on both sides of the operator are equal. Therefore, in the last expression ((b=2) ==
a), we first assigned the value 2 to b and then we compared it to a (that also stores the value
2), yielding true.
Logical operators ( !, &&, || )
The operator ! is the C++ operator for the Boolean operation NOT. It has only one operand,
to its right, and inverts it, producing false if its operand is true, and true if its operand
is false. Basically, it returns the opposite Boolean value of evaluating its operand. For
example:
1
2
3
4
!(5 == 5) // evaluates to false because the expression at its right (5
== 5) is true
!(6 <= 4) // evaluates to true because (6 <= 4) would be false
!true // evaluates to false
!false // evaluates to true
The logical operators && and || are used when evaluating two expressions to obtain a single
relational result. The operator&& corresponds to the Boolean logical operation AND, which
yields true if both its operands are true, and false otherwise. The following panel shows
the result of operator && evaluating the expression a&&b:
&& OPERATOR (and)
a b a && b
true true true
true false false
false true false
false false false
The operator || corresponds to the Boolean logical operation OR, which yields true if either
of its operands is true, thus being false only when both operands are false. Here are the
possible results of a||b:
|| OPERATOR (or)
a b a || b
true true true
true false true
false true true
false false false
For example:
1
2
( (5 == 5) && (3 > 6) ) // evaluates to false ( true && false )
( (5 == 5) || (3 > 6) ) // evaluates to true ( true || false )
When using the logical operators, C++ only evaluates what is necessary from left to right to
come up with the combined relational result, ignoring the rest. Therefore, in the last example
((5==5)||(3>6)), C++ evaluates first whether 5==5 istrue, and if so, it never checks
whether 3>6 is true or not. This is known as short-circuit evaluation, and works like this for
these operators:
operator short-circuit
&& if the left-hand side expression is false, the combined result is false (the right-hand
side expression is never evaluated).
|| if the left-hand side expression is true, the combined result is true (the right-hand
side expression is never evaluated).
This is mostly important when the right-hand expression has side effects, such as altering
values:
if ( (i<10) && (++i<n) ) { /*...*/ } // note that the condition
increments i
Here, the combined conditional expression would increase i by one, but only if the condition
on the left of && is true, because otherwise, the condition on the right-hand side (++i<n) is
never evaluated.
Conditional ternary operator ( ? )
The conditional operator evaluates an expression, returning one value if that expression
evaluates to true, and a different one if the expression evaluates as false. Its syntax is:
condition ? result1 : result2
If condition is true, the entire expression evaluates to result1, and otherwise to result2.
1
2
3
4
7==5 ? 4 : 3 // evaluates to 3, since 7 is not equal to 5.
7==5+2 ? 4 : 3 // evaluates to 4, since 7 is equal to 5+2.
5>3 ? a : b // evaluates to the value of a, since 5 is greater than
3.
a>b ? a : b // evaluates to whichever is greater, a or b.
For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// conditional operator
#include <iostream>
using namespace std;
int main ()
{
int a,b,c;
a=2;
b=7;
c = (a>b) ? a : b;
cout << c << '\n';
}
7 Edit & Run
In this example, a was 2, and b was 7, so the expression being evaluated (a>b) was not true,
thus the first value specified after the question mark was discarded in favor of the second
value (the one after the colon) which was b (with a value of 7).
Comma operator ( , )
The comma operator (,) is used to separate two or more expressions that are included where
only one expression is expected. When the set of expressions has to be evaluated for a value,
only the right-most expression is considered.
For example, the following code:
a = (b=3, b+2);
would first assign the value 3 to b, and then assign b+2 to variable a. So, at the end,
variable a would contain the value 5 while variable b would contain value 3.
Bitwise operators ( &, |, ^, ~, <<, >> )
Bitwise operators modify variables considering the bit patterns that represent the values they
store.
operator asm equivalent description
& AND Bitwise AND
| OR Bitwise inclusive OR
^ XOR Bitwise exclusive OR
~ NOT Unary complement (bit inversion)
<< SHL Shift bits left
>> SHR Shift bits right
Explicit type casting operator
Type casting operators allow to convert a value of a given type to another type. There are
several ways to do this in C++. The simplest one, which has been inherited from the C
language, is to precede the expression to be converted by the new type enclosed between
parentheses (()):
1
2
3
int i;
float f = 3.14;
i = (int) f;
The previous code converts the floating-point number 3.14 to an integer value (3); the
remainder is lost. Here, the typecasting operator was (int). Another way to do the same
thing in C++ is to use the functional notation preceding the expression to be converted by the
type and enclosing the expression between parentheses:
i = int (f);
Both ways of casting types are valid in C++.
sizeof
This operator accepts one parameter, which can be either a type or a variable, and returns the
size in bytes of that type or object:
x = sizeof (char);
Here, x is assigned the value 1, because char is a type with a size of one byte.
The value returned by sizeof is a compile-time constant, so it is always determined before
program execution.
Other operators
Later in these tutorials, we will see a few more operators, like the ones referring to pointers or
the specifics for object-oriented programming.
Precedence of operators
A single expression may have multiple operators. For example:
x = 5 + 7 % 2;
In C++, the above expression always assigns 6 to variable x, because the % operator has a
higher precedence than the +operator, and is always evaluated before. Parts of the expressions
can be enclosed in parenthesis to override this precedence order, or to make explicitly clear
the intended effect. Notice the difference:
1
2
x = 5 + (7 % 2); // x = 6 (same as without parenthesis)
x = (5 + 7) % 2; // x = 0
From greatest to smallest priority, C++ operators are evaluated in the following order: Level Precedence group Operator Description Grouping
1 Scope :: scope qualifier Left-to-right
2 Postfix (unary)
++ -- postfix increment / decrement
Left-to-right
() functional forms
[] subscript
. -> member access
3 Prefix (unary)
++ -- prefix increment / decrement
Right-to-left
~ ! bitwise NOT / logical NOT
+ - unary prefix
& * reference / dereference
new delete allocation / deallocation
sizeof parameter pack
(type) C-style type-casting
4 Pointer-to-member .* ->* access pointer Left-to-right
5 Arithmetic: scaling * / % multiply, divide, modulo Left-to-right
6 Arithmetic: addition + - addition, subtraction Left-to-right
7 Bitwise shift << >> shift left, shift right Left-to-right
8 Relational < > <= >= comparison operators Left-to-right
9 Equality == != equality / inequality Left-to-right
10 And & bitwise AND Left-to-right
11 Exclusive or ^ bitwise XOR Left-to-right
12 Inclusive or | bitwise OR Left-to-right
13 Conjunction && logical AND Left-to-right
14 Disjunction || logical OR Left-to-right
15 Assignment-level expressions
= *= /= %= +=
-=
>>= <<= &= ^=
|=
assignment / compound assignment
Right-to-left
?: conditional operator
16 Sequencing , comma separator Left-to-right
When an expression has two operators with the same precedence level, grouping determines
which one is evaluated first: either left-to-right or right-to-left.
11.Scope
A scope is a region of the program and broadly speaking there are three places, where variables can be declared:
Inside a function or a block which is called local variables,
In the definition of function parameters which is called formal parameters.
Outside of all functions which is called global variables.
We will learn what is a function and it's parameter in subsequent chapters. Here let us explain what are local and global variables.
6. Массивы, освобождение массива
• 6. Arrays, release the array
Arrays An array is a series of elements of the same type placed in contiguous memory locations that
can be individually referenced by adding an index to a unique identifier.
That means that, for example, five values of type int can be declared as an array without
having to declare 5 different variables (each with its own identifier). Instead, using an array,
the five int values are stored in contiguous memory locations, and all five can be accessed
using the same identifier, with the proper index.
For example, an array containing 5 integer values of type int called foo could be represented
as:
where each blank panel represents an element of the array. In this case, these are values of
type int. These elements are numbered from 0 to 4, being 0 the first and 4 the last; In C++,
the first element in an array is always numbered with a zero (not a one), no matter its length.
Like a regular variable, an array must be declared before it is used. A typical declaration for
an array in C++ is:
type name [elements];
where type is a valid type (such as int, float...), name is a valid identifier and
the elements field (which is always enclosed in square brackets []), specifies the length of
the array in terms of the number of elements.
Therefore, the foo array, with five elements of type int, can be declared as:
int foo [5];
NOTE: The elements field within square brackets [], representing the number of
elements in the array, must be aconstant expression, since arrays are blocks of static
memory whose size must be determined at compile time, before the program runs.
int foo [5] = { 16, 2, 77, 40, 12071 };
Multidimensional arrays
Multidimensional arrays can be described as "arrays of arrays". For example, a bidimensional
array can be imagined as a two-dimensional table made of elements, all of them of a same
uniform data type.
jimmy represents a bidimensional array of 3 per 5 elements of type int. The C++ syntax for
this is:
int jimmy [3][5];
and, for example, the way to reference the second element vertically and fourth horizontally
in an expression would be:
jimmy[1][3]
(remember that array indices always begin with zero).
Multidimensional arrays are not limited to two indices (i.e., two dimensions). They can
contain as many indices as needed. Although be careful: the amount of memory needed for an
array increases exponentially with each dimension. For example:
char century [100][365][24][60][60];
declares an array with an element of type char for each second in a century. This amounts to
more than 3 billion char! So this declaration would consume more than 3 gigabytes of
memory!
At the end, multidimensional arrays are just an abstraction for programmers, since the same
results can be achieved with a simple array, by multiplying its indices:
1
2
int jimmy [3][5]; // is equivalent to
int jimmy [15]; // (3 * 5 = 15)
With the only difference that with multidimensional arrays, the compiler automatically
remembers the depth of each imaginary dimension. The following two pieces of code
produce the exact same result, but one uses a bidimensional array while the other uses a
simple array:
multidimensional array pseudo-multidimensional array
#define WIDTH 5
#define HEIGHT 3
int jimmy [HEIGHT][WIDTH];
int n,m;
int main ()
{
for (n=0; n<HEIGHT; n++)
for (m=0; m<WIDTH; m++)
{
jimmy[n][m]=(n+1)*(m+1);
}
}
#define WIDTH 5
#define HEIGHT 3
int jimmy [HEIGHT * WIDTH];
int n,m;
int main ()
{
for (n=0; n<HEIGHT; n++)
for (m=0; m<WIDTH; m++)
{
jimmy[n*WIDTH+m]=(n+1)*(m+1);
}
}
None of the two code snippets above produce any output on the screen, but both assign
values to the memory block called jimmy in the following way:
Note that the code uses defined constants for the width and height, instead of using directly
their numerical values. This gives the code a better readability, and allows changes in the
code to be made easily in one place.
Arrays as parameters
At some point, we may need to pass an array to a function as a parameter. In C++, it is not
possible to pass the entire block of memory represented by an array to a function directly as
an argument. But what can be passed instead is its address. In practice, this has almost the
same effect, and it is a much faster and more efficient operation.
To accept an array as parameter for a function, the parameters can be declared as the array
type, but with empty brackets, omitting the actual size of the array. For example:
void procedure (int arg[])
This function accepts a parameter of type "array of int" called arg. In order to pass to this
function an array declared as:
int myarray [40];
it would be enough to write a call like this:
procedure (myarray);
Here you have a complete example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// arrays as parameters
#include <iostream>
using namespace std;
void printarray (int arg[], int length) {
for (int n=0; n<length; ++n)
cout << arg[n] << ' ';
cout << '\n';
}
int main ()
{
int firstarray[] = {5, 10, 15};
int secondarray[] = {2, 4, 6, 8, 10};
printarray (firstarray,3);
printarray (secondarray,5);
}
5 10 15
2 4 6 8 10
Edit & Run
In the code above, the first parameter (int arg[]) accepts any array whose elements are of
type int, whatever its length. For that reason, we have included a second parameter that tells
the function the length of each array that we pass to it as its first parameter. This allows the
for loop that prints out the array to know the range to iterate in the array passed, without
going out of range.
In a function declaration, it is also possible to include multidimensional arrays. The format
for a tridimensional array parameter is:
base_type[][depth][depth]
For example, a function with a multidimensional array as argument could be:
void procedure (int myarray[][3][4])
Notice that the first brackets [] are left empty, while the following ones specify sizes for
their respective dimensions. This is necessary in order for the compiler to be able to
determine the depth of each additional dimension.
In a way, passing an array as argument always loses a dimension. The reason behind is that,
for historical reasons, arrays cannot be directly copied, and thus what is really passed is a
pointer. This is a common source of errors for novice programmers. Although a clear
understanding of pointers, explained in a coming chapter, helps a lot.
Library arrays
The arrays explained above are directly implemented as a language feature, inherited from
the C language. They are a great feature, but by restricting its copy and easily decay into
pointers, they probably suffer from an excess of optimization.
To overcome some of these issues with language built-in arrays, C++ provides an alternative
array type as a standard container. It is a type template (a class template, in fact) defined in
header <array>.
Containers are a library feature that falls out of the scope of this tutorial, and thus the class
will not be explained in detail here. Suffice it to say that they operate in a similar way to
built-in arrays, except that they allow being copied (an actually expensive operation that
copies the entire block of memory, and thus to use with care) and decay into pointers only
when explicitly told to do so (by means of its member data).
Just as an example, these are two versions of the same example using the language built-in
array described in this chapter, and the container in the library:
language built-in array container library array
#include <iostream>
using namespace std;
int main()
{
int myarray[3] = {10,20,30};
for (int i=0; i<3; ++i)
++myarray[i];
for (int elem : myarray)
cout << elem << '\n';
}
#include <iostream>
#include <array>
using namespace std;
int main()
{
array<int,3> myarray {10,20,30};
for (int i=0; i<myarray.size(); ++i)
++myarray[i];
for (int elem : myarray)
cout << elem << '\n';
}
As you can see, both kinds of arrays use the same syntax to access its
elements: myarray[i]. Other than that, the main differences lay on the declaration of the
array, and the inclusion of an additional header for the library array. Notice also how it
is easy to access the size of the library array.
Operator delete[]
Deallocate storage space of array
8. Произвольное количество параметров в функции
8. arbitrary number of parameters in the function
In C++, two different functions can have the same name if their parameters are different; either
because they have a different number of parameters, or because any of their parameters are of a
different type. For example:
For cases such as this, C++ has the ability to define functions with generic types, known
as function templates. Defining a function template follows the same syntax than a regular
function, except that it is preceded by the template keyword and a series of template
parameters enclosed in angle-brackets <>:
template <template-parameters> function-declaration
The template parameters are a series of parameters separated by commas. These parameters
can be generic template types by specifying either the class or typename keyword followed
by an identifier. This identifier can then be used in the function declaration as if it was a
regular type. For example, a generic sum function could be defined as:
1
2
3
4
5
template <class SomeType>
SomeType sum (SomeType a, SomeType b)
{
return a+b; }
9. if, while, for, switch
Syntax:
(selection statement) The syntax of an if...else if...else statement in C++ is:
if(boolean_expression 1)
{
// Executes when the boolean expression 1 is true
}
else if( boolean_expression 2)
{
// Executes when the boolean expression 2 is true
}
else if( boolean_expression 3)
{
// Executes when the boolean expression 3 is true
}
else
{
// executes when the none of the above condition is true.
}
(Iteration Statement) Loops repeat a statement a certain number of times, or while a condition is fulfilled. They are
introduced by the keywordswhile, do, and for.
The while loop
The simplest kind of loop is the while-loop. Its syntax is:
while (expression) statement
The while-loop simply repeats statement while expression is true. If, after any execution
of statement, expression is no longer true, the loop ends, and the program continues right
after the loop. For example, let's have a look at a countdown using a while-loop:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// custom countdown using while
#include <iostream>
using namespace std;
int main ()
{
int n = 10;
while (n>0) {
cout << n << ", ";
--n;
}
cout << "liftoff!\n";
}
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, liftoff!
A switch statement allows a variable to be tested for equality against a list of values. Each value is called a case, and the variable being switched on is checked for each case.
Syntax: The syntax for a switch statement in C++ is as follows:
switch(expression){
case constant-expression :
statement(s);
break; //optional
case constant-expression :
statement(s);
break; //optional
// you can have any number of case statements.
default : //Optional
statement(s);
}
10. Описание класса
• 10. class description
#pragma once #include <string> #include <vector> using namespace std; #ifndef H_BANK #define H_BANK //Indicate that there exist a class called AccountHolder, //but we don't want to use #include because we may be //creating circular references class AccountHolder; class Bank { public: Bank(string name, double initial_amount = 0.0) { m_bank_name = name; m_bank_money = initial_amount; } ~Bank(void) {} string get_bank_name() { return m_bank_name; } int get_bank_id() { return m_bank_id; } void AddNewCustomer(AccountHolder* customer) { m_customer_list.push_back(customer); } void DeleteCustomer(string customer_name); AccountHolder* GetCustomer(string customer_name); void displayCustomers(); void displayCustomersWithBalance(); private: //Balance on bank accounts affect the money on the bank //so, we need to make BankAccount a "friend class" so that //it will be able to change the bank money, which is private friend class BankAccount; int m_bank_id; string m_bank_name; double m_bank_money; bool Deposit(double amount) { if (amount <= 0.0) return false; m_bank_money += amount; return true; } bool Withdraw(double amount)
{ if (amount <= 0.0) return false; if (m_bank_money < amount) return false; m_bank_money -= amount; return true; } vector<AccountHolder*> m_customer_list; }; #endif
Classes & Objects in Detail: So far, you have got very basic idea about C++ Classes and Objects. There are further interesting concepts related to C++ Classes and Objects which we will discuss in various sub-sections listed below:
Concept Description
Class member functions
A member function of a class is a function that has its
definition or its prototype within the class definition like any
other variable.
Class access modifiers
A class member can be defined as public, private or
protected. By default members would be assumed as private.
Constructor & destructor
A class constructor is a special function in a class that is
called when a new object of the class is created. A destructor
is also a special function which is called when created object
is deleted.
C++ copy constructor
The copy constructor is a constructor which creates an object
by initializing it with an object of the same class, which has
been created previously.
C++ friend functions
A friend function is permitted full access to private and
protected members of a class.
C++ inline functions
With an inline function, the compiler tries to expand the code
in the body of the function in place of a call to the function.
The this pointer in C++
Every object has a special pointer this which points to the
object itself.
Pointer to C++ classes
A pointer to a class is done exactly the same way a pointer to
a structure is. In fact a class is really just a structure with
functions in it.
Static members of a class
Both data members and function members of a class can be
declared as static.
11. Области видимости членов класса
• 11. Scopes class members
Classes have the same format as plain data structures, except that they can also include
functions and have these new things called access specifiers. An access specifier is one of the
following three keywords: private, public or protected. These specifiers modify the
access rights for the members that follow them:
private members of a class are accessible only from within other members of the
same class (or from their"friends").
protected members are accessible from other members of the same class (or from
their "friends"), but also from members of their derived classes.
Finally, public members are accessible from anywhere where the object is visible.
By default, all members of a class declared with the class keyword have private access for
all its members. Therefore, any member that is declared before any other access specifier has
private access automatically. For example:
1
2
3
4
5
6
class Rectangle {
int width, height;
public:
void set_values (int,int);
int area (void);
} rect;
15. Constructors, destructors.
Parameterized Constructor: A default constructor does not have any parameter, but if you need, a constructor can have parameters. This helps you to assign initial value to an object at the time of its creation as shown in the following example:
#pragma once #include <string> #include <vector> using namespace std; #ifndef H_BANK #define H_BANK //Indicate that there exist a class called AccountHolder, //but we don't want to use #include because we may be //creating circular references class AccountHolder; class Bank { public: Bank(string name, double initial_amount = 0.0) { m_bank_name = name; m_bank_money = initial_amount; } ~Bank(void) {} string get_bank_name() { return m_bank_name; } int get_bank_id() { return m_bank_id; }
A destructor is a special member function that is called when the lifetime of an object ends. The purpose of the destructor is to free the resources that the object may have acquired
during its lifetime.
The destructor is commonly used to "clean up" when an object is no longer necessary. Consider the
following declaration of a String class:
// spec1_destructors.cpp
#include <string.h>
class String {
public:
String( char *ch ); // Declare constructor
~String(); // and destructor.
private:
char *_text;
size_t sizeOfText;
};
// Define the constructor.
String::String( char *ch ) {
sizeOfText = strlen( ch ) + 1;
// Dynamically allocate the correct amount of memory.
_text = new char[ sizeOfText ];
// If the allocation succeeds, copy the initialization string.
if( _text )
strcpy_s( _text, sizeOfText, ch );
}
// Define the destructor.
String::~String() {
// Deallocate the memory that was previously reserved
// for this string.
if (_text)
delete[] _text;
}
int main() {
String str("The piper in the glen...");
}
In the preceding example, the destructor String::~String uses the delete operator to deallocate
the space dynamically allocated for text storage.
22) Friend Functions and Friend Classes
Friendship and inheritance
Friend functions
In principle, private and protected members of a class cannot be accessed from outside the
same class in which they are declared. However, this rule does not apply to "friends".
Friends are functions or classes declared with the friend keyword.
A non-member function can access the private and protected members of a class if it is
declared a friend of that class. That is done by including a declaration of this external
function within the class, and preceding it with the keywordfriend:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// friend functions
#include <iostream>
using namespace std;
class Rectangle {
int width, height;
public:
Rectangle() {}
Rectangle (int x, int y) : width(x),
height(y) {}
int area() {return width * height;}
friend Rectangle duplicate (const
Rectangle&);
};
Rectangle duplicate (const Rectangle&
param)
{
Rectangle res;
res.width = param.width*2;
res.height = param.height*2;
return res;
}
int main () {
Rectangle foo;
Rectangle bar (2,3);
foo = duplicate (bar);
cout << foo.area() << '\n';
return 0;
}
24 Edit & Run
The duplicate function is a friend of class Rectangle. Therefore, function duplicate is
able to access the members widthand height (which are private) of different objects of
type Rectangle. Notice though that neither in the declaration ofduplicate nor in its later
use in main, member function duplicate is considered a member of class Rectangle. It
isn't! It simply has access to its private and protected members without being a member.
Typical use cases of friend functions are operations that are conducted between two different
classes accessing private or protected members of both.
Friend classes
Similar to friend functions, a friend class is a class whose members have access to the private
or protected members of another class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// friend class
#include <iostream>
using namespace std;
class Square;
class Rectangle {
int width, height;
public:
int area ()
{return (width * height);}
void convert (Square a);
};
class Square {
friend class Rectangle;
private:
int side;
public:
Square (int a) : side(a) {}
};
void Rectangle::convert (Square a) {
width = a.side;
height = a.side;
}
int main () {
Rectangle rect;
Square sqr (4);
rect.convert(sqr);
cout << rect.area();
return 0;
}
16 Edit & Run
In this example, class Rectangle is a friend of class Square allowing Rectangle's member
functions to access private and protected members of Square. More
concretely, Rectangle accesses the member variable Square::side, which describes the
side of the square.
There is something else new in this example: at the beginning of the program, there is an
empty declaration of classSquare. This is necessary because
class Rectangle uses Square (as a parameter in member convert),
and Square usesRectangle (declaring it a friend).
Friendships are never corresponded unless specified: In our example, Rectangle is
considered a friend class by Square, but Square is not considered a friend by Rectangle.
Therefore, the member functions of Rectangle can access the protected and private members
of Square but not the other way around. Of course, Square could also be declared friend
ofRectangle, if needed, granting such an access.
Another property of friendships is that they are not transitive: The friend of a friend is not
considered a friend unless explicitly specified.
• 29. syntax exception handling
• 30. Syntax throwing exceptions
Throwing an Exception One way of handling complex error conditions is with exceptions. An exception is a value
or an object that signals an error. When the error occurs, an exception is said to be
“thrown” because control will pass to a part of the program that catches and handles
that
type of error. For example, the following code shows the divide function, modified to
throw an exception when division by zero has been attempted. double divide(double numerator, double denominator) { if (denominator == 0) throw "ERROR: Cannot divide by zero.\n"; else return numerator / denominator; }
The following statement causes the exception to be thrown. throw "ERROR: Cannot divide by zero.\n";
The throw key word is followed by an argument, which can be any value. As you will see,
the type of the argument is used to determine the nature of the error. The function above
simply throws a string containing an error message.
The line containing a throw statement is known as the throw point. When a throw statement
is executed, control is passed to another part of the program known as an exception
handler.
Handling an Exception To handle an exception, a program must have a try/catch construct. The general format of
the try/catch construct is try
{ // code here calls functions or object member // functions that might throw an exception. } catch(exception parameter) { // code here handles the exception }
// Repeat as many catch blocks as needed.
The first part of the construct is the try block. This starts with the key word try and is
followed
by a block of code executing any statements that might directly or indirectly cause
an exception to be thrown. The try block is immediately followed by one or more catch
blocks, which are the exception handlers. A catch block starts with the key word catch,
followed by a set of parentheses containing the declaration of an exception parameter. For
example, here is a try/catch construct that can be used with the divide function: try { quotient = divide(num1, num2); cout << "The quotient is " << quotient << endl; }
catch (char *exceptionString) { cout << exceptionString; }
• 32. Constructors and Exceptions
Extracting Information from the Exception Class
• 31. Standard exclusions
12. inheritance (classes)
Inheritance and the Is-a Relationship When one object is a specialized version of another object, there is an is-a relationship
between them. For example, a grasshopper is an insect. Here are a few other examples of
the is-a relationship.
• A poodle is a dog.
• A car is a vehicle.
• A rectangle is a shape.
13. Multiple Inheritance
#include <iostream>
using std::ostream;
using std::cout;
using std::endl;
class Base1 {
public:
Base1( int parameterValue )
{
value = parameterValue;
}
int getData() const
{
return value;
}
protected:
int value;
};
class Base2
{
public:
Base2( char characterData )
{
letter = characterData;
}
char getData() const
{
return letter;
}
protected:
char letter;
};
class Derived : public Base1, public Base2
{
public:
Derived( int integer, char character, double double1 )
: Base1( integer ), Base2( character ), real( double1 ) { }
double getReal() const {
return real;
}
void display()
{
cout << " Integer: " << value << "\n Character: "
<< letter << "\nReal number: " << real;
}
private:
double real;
};
int main()
{
Base1 base1( 10 ), *base1Ptr = 0;
Base2 base2( 'Z' ), *base2Ptr = 0;
Derived derived( 7, 'A', 3.5 );
cout << base1.getData()
<< base2.getData();
derived.display();
cout << derived.Base1::getData()
<< derived.Base2::getData()
<< derived.getReal() << "\n\n";
base1Ptr = &derived;
cout << base1Ptr->getData() << '\n';
base2Ptr = &derived;
cout << base2Ptr->getData() << endl;
return 0;
}
10Z Integer: 7
Character: A
Real number: 3.57A3.5
7
A
14.virtual inheritance
The "diamond problem" (sometimes referred to as the "deadly diamond of death" [5]) is an
ambiguity that arises when two classes B and C inherit from A, and class D inherits from both B
and C. If there is a method in A that B and/or C hasoverridden, and D does not override it, then
which version of the method does D inherit: that of B, or that of C?
#include <iostream> using namespace std; class Animal{ public: int age; void walk() { cout<<"animal walks"<<endl; } }; class Tiger : public Animal { }; class Lion : public Animal { }; class Liger: public Tiger, public Lion { }; int main() { Liger anil; anil.walk(); // is ambigious error! return 0; }
Deadly Diamond problem
Check theconstructor call too! How it’s call all of them
#include <iostream> using namespace std; class Animal{ public: int age; void walk() {
cout<<"animal walks"<<endl; } }; class Tiger : virtual public Animal { public: Tiger() { cout<<"constructor of tiger"<<endl; } }; class Lion :virtual public Animal { public: Lion() { cout<<"constructor of Lion"<<endl; } }; class Liger: public Tiger, public Lion { }; int main() { Liger anil; anil.walk(); getchar(); return 0; }
Polymorphism The word polymorphism means having many forms. Typically, polymorphism occurs when there is a hierarchy of classes and they are related by inheritance.
C++ polymorphism means that a call to a member function will cause a different function to be executed depending on the type of object that invokes the function.
Consider the following example where a base class has been derived by other two classes:
#include <iostream>
using namespace std;
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
};
class Rectangle: public Shape{
public:
Rectangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Rectangle class area :" <<endl;
return (width * height);
}
};
class Triangle: public Shape{
public:
Triangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Triangle class area :" <<endl;
return (width * height / 2);
}
};
// Main function for the program
int main( )
{
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);
// store the address of Rectangle
shape = &rec;
// call rectangle area.
shape->area();
// store the address of Triangle
shape = &tri;
// call triangle area.
shape->area();
return 0;
}
When the above code is compiled and executed, it produces the following result:
Parent class area
Parent class area
The reason for the incorrect output is that the call of the function area() is being set once by the compiler as the version defined in the base class. This is called static resolution of the function call, or static linkage - the function call is fixed before the program is executed. This is also sometimes called early binding because the area() function is set during the compilation of the program. But now, let's make a slight modification in our program and precede the declaration of area() in the Shape class with the keyword virtual so that it looks like this:
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
virtual int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
};
After this slight modification, when the previous example code is compiled and executed, it produces the following result:
Rectangle class area
Triangle class area
This time, the compiler looks at the contents of the pointer instead of it's type. Hence, since addresses of objects of tri and rec classes are stored in *shape the respective area() function is called.
As you can see, each of the child classes has a separate implementation for the function area(). This is how polymorphism is generally used. You have different classes with a function of the same name, and even the same parameters, but with different implementations.
Virtual Function: A virtual function is a function in a base class that is declared using the keyword virtual. Defining in a base class a virtual function, with another version in a derived class, signals to the compiler that we don't want static linkage for this function. What we do want is the selection of the function to be called at any given point in the program to be based on the kind of object for which it is called. This sort of operation is referred to as dynamic linkage, or late binding.
Pure Virtual Functions: It's possible that you'd want to include a virtual function in a base class so that it may be redefined in a derived class to suit the objects of that class, but that there is no meaningful definition you could give for the function in the base class.
We can change the virtual function area() in the base class to the following:
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
// pure virtual function
virtual int area() = 0;
};
The = 0 tells the compiler that the function has no body and above virtual function will be called pure virtual function.