Вы находитесь на странице: 1из 20

NESTING

Classes can be defined inside other classes. Classes that are defined inside other classes are
called nested classes. Nested classes are used in situations where the nested class has a close conceptual
relationship to its surrounding class. For example, with the class string a type string::iterator is available
which will provide all elements (characters) that are stored in the string. This string::iterator type could be
defined as an object iterator, defined as nested class in the class string.
A class can be nested in every part of the surrounding class: in the public, protected or private section. Such
a nested class can be considered a member of the surrounding class. The normal access and rules in classes
apply to nested classes. If a class is nested in the public section of a class, it is visible outside the
surrounding class. If it is nested in the protected section it is visible in subclasses, derived from the
surrounding class (see chapter 13), if it is nested in the private section, it is only visible for the members of
the surrounding class.
The surrounding class has no special privileges with respect to the nested class. So, the nested class still has
full control over the accessibility of its members by the surrounding class. For example, consider the
following class definition:
class Surround
{
public:
class FirstWithin
{
int d_variable;
public:
FirstWithin();
int var() const
{
return d_variable;
}
};
private:
class SecondWithin
{
int d_variable;
public:
SecondWithin();
int var() const
{
return d_variable;
}
};
};
In this definition access to the members is defined as follows:

The class FirstWithin is visible both outside and inside Surround. The class FirstWithin therefore has
global scope.

The constructor FirstWithin() and the member function var() of the class FirstWithin are also
globally visible.

The int d_variable datamember is only visible to the members of the class FirstWithin. Neither the
members of Surround nor the members of SecondWithin can access d_variable of the
classFirstWithin directly.

The class SecondWithin is only visible inside Surround. The public members of the
class SecondWithin can also be used by the members of the class FirstWithin, as nested classes can
be considered members of their surrounding class.

The constructor SecondWithin() and the member function var() of the class SecondWithin can also
only be reached by the members of Surround (and by the members of its nested classes).

The int d_variable datamember of the class SecondWithin is only visible to the members of the
class SecondWithin. Neither the members of Surround nor the members of FirstWithin can
accessd_variable of the class SecondWithin directly.

As always, an object of the class type is required before its members can be called. This also holds
true for nested classes.

If the surrounding class should have access rights to the private members of its nested classes or if nested
classes should have access rights to the private members of the surrounding class, the classes can be defined
as friend classes.
The nested classes can be considered members of the surrounding class, but the members of nested classes
are not members of the surrounding class. So, a member of the class Surround may not
accessFirstWithin::var() directly. This is understandable considering the fact that a Surround object is not
also a FirstWithin or SecondWithin object. In fact, nested classes are just typenames. It is not implied that
objects of such classes automatically exist in the surrounding class. If a member of the surrounding class
should use a (non-static) member of a nested class then the surrounding class must define a nested class
object, which can thereupon be used by the members of the surrounding class to use members of the nested
class.
For example, in the following class definition there is a surrounding class Outer and a nested class Inner.
The class Outer contains a member function caller() which uses the inner object that is composed inOuter to
call the infunction() member function of Inner:
class Outer
{
public:
void caller()
{
d_inner.infunction();
}
private:

class Inner
{
public:
void infunction();
};
Inner d_inner; // class Inner must be known
};
The mentioned function Inner::infunction() can be called as part of the inline definition of Outer::caller(),
even though the definition of the class Inner is yet to be seen by the compiler. On the other hand, the
compiler must have seen the definition of the class Inner before a data member of that class can be defined.
Defining nested class members
Member functions of nested classes may be defined as inline functions. Inline member functions can be
defined as if they were functions defined outside of the class definition: if the function Outer::caller()would
have been defined outside of the class Outer, the full class definition (including the definition of the
class Inner) would have been available to the compiler. In that situation the function is perfectly compilable.
Inline functions can be compiled accordingly: they can be defined and they can use any nested class. Even if
it appears later in the class interface.
Member functions of nested classes can also be defined outside of their surrounding class. Consider the
constructor of the class FirstWithin in the example of the previous section. The constructorFirstWithin() is
defined in the class FirstWithin, which is, in turn, defined within the class Surround. Consequently, the class
scopes of the two classes must be used to define the constructor. E.g.,
Surround::FirstWithin::FirstWithin()
{
variable = 0;
}
Declaring nested classes
Nested classes may be declared before they are actually defined in a surrounding class. Such forward
declarations are required if a class contains multiple nested classes, and the nested classes contain pointers,
references, parameters or return values to objects of the other nested classes.
For example, the following class Outer contains two nested classes Inner1 and Inner2. The
class Inner1 contains a pointer to Inner2 objects, and Inner2 contains a pointer to Inner1 objects. Such cross
references require forward declarations. These forward declarations must be specified in the same accesscategory as their actual definitions. In the following example the Inner2 forward declaration must be given
in a private section, as its definition is also part of the class Outer's private interface:
ARRAY OF STRUCTURES
As discussed in the previous post, there are two types of data structures available to C/C++ programmers.
One is already built into the programming language and other one is a bit complex in a sense that it can be
implemented using the built in data structures and data types. In C/C++ programming language, built in data
structures include Arrays, structures, unions and classes. Some of the examples of complex data structures
are Stack, Queue, Linked List, Tree and Graph.

The aim of this first tutorials is to teach you how to declare, initialise and use simple arrays as well as
multidimensional arrays. You will also be able to use arrays as data structure in your C/C++ program. So at
the end of this tutorial you will be able to answer:

What is an array and how you can use it?


How to declare and initialise simple arrays?
How to declare and initialise multidimensional arrays?
How to perform simple operations on arrays?

What is an array?
Array is a very basic data structure provided by every programming language. Lets talk about an example
scenario where we need to store ten employees data in our C/C++ program including name, age and salary.
One of the solutions is to declare ten different variables to store employee name and ten more to store age
and so on. Also you will need some sort of mechanism to get information about an employee, search
employee records and sort them. To solve these types of problem C/C++ provide a mechanism called Arrays.
Definition
An array is simply a number of memory locations, each of which can store an item of data of the same data
type and which are all referenced through the same variable name. Ivor Horton.
Array may be defined abstractly as finite order set of homogeneous elements. So we can say that there are
finite numbers of elements in an array and all the elements are of same data type. Also array elements are
ordered i.e. we can access a specific array element by an index.
How to declare an array?
The general form of declaring a simple (one dimensional) array is
array_type variable_name[array_size];
in your C/C++ program you can declare an array like
int Age[10];
Here array_type declares base type of array which is the type of each element in array. In our example
array_type is int and its name is Age. Size of the array is defined by array_size i.e. 10. We can access array
elements by index, and first item in array is at index 0. First element of array is called lower bound and its
always 0. Highest element in array is called upper bound.
In C programming language upper and lower bounds cannot be changed during the execution of the
program, so array length can be set only when the program in written.

Age 0

Age 1

Age 2

Age 3

Age 4

Age 5

Age 6

Age 7

Age 8

Age 9

30

32

54

32

26

29

23

43

34

Array has 10 elements


Note: One good practice is to declare array length as a constant identifier. This will minimise the required
work to change the array size during program development.
Considering the array we declared above we can declare it like
#define NUM_EMPLOYEE 10
int Age[NUM_EMPLOYEE];
How to initialise an array?
Initialisation of array is very simple in c programming. There are two ways you can initialise arrays.

Declare and initialise array in one statement.


Declare and initialise array separately.

Look at the following C code which demonstrates the declaration and initialisation of an array.
int Age [5] = {30, 22, 33, 44, 25};
int Age [5];
Age [0]=30;
Age [1]=22;
Age [2]=33;
Age [3]=44;
Age [4]=25;
Array can also be initialised in a ways that array size is omitted, in such case compiler automatically
allocates memory to array.
int Age [] = {30, 22, 33, 44, 25};
Lets write a simple program that uses arrays to print out number of employees having salary more than
3000.
Array in C Programming
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_EMPLOYEE 10

int main(int argc, char *argv[]){


int Salary[NUM_EMPLOYEE], lCount=0,gCount=0,i=0;
printf("Enter employee salary (Max 10)\n ");
for (i=0; i<NUM_EMPLOYEE; i++){
printf("\nEnter employee salary: %d - ",i+1);
scanf("%d",&Salary[i]);
}
for(i=0; i<NUM_EMPLOYEE; i++){
if(Salary[i]<3000)
lCount++;
else
gCount++;
}
printf("\nThere are {%d} employee with salary more than 3000\n",gCount);
printf("There are {%d} employee with salary less than 3000\n",lCount);
printf("Press ENTER to continue...\n");
getchar();
return 0;
}
Array in C++ Programming
#include <cstdlib>
#include <iostream>
#define NUM_EMPLOYEE 10
using namespace std;
int main(int argc, char *argv[]){
int Salary[NUM_EMPLOYEE], lCount=0,gCount=0,i=0;
cout << "Enter employee salary (Max 10) " << endl;
for (i=0; i<NUM_EMPLOYEE; i++){
cout << "Enter employee salary: - " << i+1 << endl;
cin >> Salary[i];
}
for(i=0; i<NUM_EMPLOYEE; i++){
if(Salary[i]<3000)
lCount++;
else
gCount++;
}

cout << "There are " << gCount << " employee with salary more than 3000" << endl
<< "There are " << lCount << " employee with salary less than 3000" << endl;
system("PAUSE");
return EXIT_SUCCESS;
}
How to declare and initialise multidimensional arrays?
Often there is need to manipulate tabular data or matrices. For example if employee salary is increased by
20% and you are required to store both the salaries in your program. Then you will need to store this
information into a two dimensional arrays. C/C++ gives you the ability to have arrays of any dimension.
Multi dimension arrays
Consider the example above, you have to store, previous salary, present salary and amount of increment. In
that case you will need to store this information in three dimensional arrays.
First I will show you how to declare a two dimensional array and initialise it. Then write a complete
program to use multidimensional arrays.
int Salary[10][2];
This defines an array containing 10 elements of type int. Each of these elements itself is an array of two
integers. So to keep track of each element of this array is we have to use two indices. One is to keep track of
row and other is to keep track of column.
Elements of multidimensional arrays
Here is a graphical view of multidimensional array that we use to store salary and increment on salary. First
column stores the salary element of the array and second column stores increment on salary. We could add
another column to store the new salary which adds the increment to the salary.

Column 0 Column 1
Salary
Increment
Row 0
Row 2
Row 3
Row 4
Row 5

Row 6
Row 7
Row 8
Row 9
Row 10
Initialising multidimensional arrays
Multidimensional arrays can also be initialised in two ways just like one dimensional array. Two braces are
used to surround the row element of arrays.
If you are initialising more than one dimension then you will have to use as many braces as the dimensions
of the array are.
int Salary [5][2] = {
{2300, 460},
{3400, 680},
{3200, 640},
{1200, 240},
{3450, 690}
};
int Salary [5][2] ={0}; //This will initialise all the array elements to 0
int Salary [5][2];
Salary [0][0]=2300;
Salary [1][0]=3400;
Salary [2][0]=3200;
Salary [3][0]=1200;
Salary [4][0]=3450;
Salary [0][1]=460;
Salary [1][1]=680;
Salary [2][1]=640;
Salary [3][1]=240;
Salary [4][1]=690;
Here is a complete program written in both C and C++ to demonstrate the use of multidimensional arrays.
You can find the whole source code in a zip file at the end of the tutorial. Source code is available in both C
and C++ programming languages. Zip file also contains the demonstration of three dimensional arrays.
Demonstration of two dimension arrays

The code below demonstrates two dimension arrays. It uses the same example of employee salary to
increment it by 20% and adds it to actual salary then print current salary, increment and new salary.
Two dimensional Array in C Programming
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_EMPLOYEE 10
int main(int argc, char *argv[]){
//initialise Salary of each employee
int Salary[NUM_EMPLOYEE][2]={
{2300,0},
{3400,0},
{3200,0},
{1200,0},
{3450,0},
{3800,0},
{3900,0},
{2680,0},
{3340,0},
{3000,0}
};
int lCount=0,gCount=0,i=0;
for(i=0; i<NUM_EMPLOYEE; i++){
Salary[i][1] = ((Salary[i][0]*20)/100);
}
printf("Initial Salary + Increment = Total Salary\n");
for (i=0; i<NUM_EMPLOYEE; i++){
printf("%d\t\t%d\t\t%d\n",Salary[i][0],Salary[i][1],Salary[i][0]+Salary[i][1]);
}
printf("Press ENTER to continue...\n");
getchar();
return 0;
}
Two dimensional array in C++ Programming
#include <cstdlib>
#include <iostream>
#define NUM_EMPLOYEE 10
using namespace std;
int main(int argc, char *argv[]){

//initialise Salary of each employee


int Salary[NUM_EMPLOYEE][2]={
{2300,0},
{3400,0},
{3200,0},
{1200,0},
{3450,0},
{3800,0},
{3900,0},
{2680,0},
{3340,0},
{3000,0}
};
int lCount=0,gCount=0,i=0;
for(i=0; i<NUM_EMPLOYEE; i++){
Salary[i][1] = ((Salary[i][0]*20)/100);
}
cout << "Initial Salary + Increment = Total Salary" << endl;
for (i=0; i<NUM_EMPLOYEE; i++){
printf("%d\t\t%d\t\t%d\n",Salary[i][0],Salary[i][1],Salary[i][0]+Salary[i][1]);
}
system("PAUSE");
return EXIT_SUCCESS;
}

POINTERS
Pointers in C are easy and fun to learn. Some C programming tasks are performed more easily with pointers,
and other tasks, such as dynamic memory allocation, cannot be performed without using pointers. So it
becomes necessary to learn pointers to become a perfect C programmer. Let's start learning them in simple
and easy steps.
As you know, every variable is a memory location and every memory location has its address defined which
can be accessed using ampersand (&) operator, which denotes an address in memory. Consider the following
example, which will print the address of the variables defined:
#include <stdio.h>
int main ()
{
int var1;
char var2[10];

printf("Address of var1 variable: %x\n", &var1 );


printf("Address of var2 variable: %x\n", &var2 );
return 0;
}
When the above code is compiled and executed, it produces result something as follows:
Address of var1 variable: bff5a400
Address of var2 variable: bff5a3f6
So you understood what is memory address and how to access it, so base of the concept is over. Now let us
see what is a pointer.
What Are Pointers?
A pointer is a variable whose value is the address of another variable, i.e., direct address of the memory
location. Like any variable or constant, you must declare a pointer before you can use it to store any variable
address. The general form of a pointer variable declaration is:
type *var-name;
Here, type is the pointer's base type; it must be a valid C data type and var-name is the name of the pointer
variable. The asterisk * you used to declare a pointer is the same asterisk that you use for multiplication.
However, in this statement the asterisk is being used to designate a variable as a pointer. Following are the
valid pointer declaration:
int *ip; /* pointer to an integer */
double *dp; /* pointer to a double */
float *fp; /* pointer to a float */
char *ch /* pointer to a character */
The actual data type of the value of all pointers, whether integer, float, character, or otherwise, is the same, a
long hexadecimal number that represents a memory address. The only difference between pointers of
different data types is the data type of the variable or constant that the pointer points to.
How to use Pointers?
There are few important operations, which we will do with the help of pointers very frequently. (a) we
define a pointer variable (b) assign the address of a variable to a pointer and (c) finally access the value at
the address available in the pointer variable. This is done by using unary operator * that returns the value of
the variable located at the address specified by its operand. Following example makes use of these
operations:
#include <stdio.h>
int main ()
{
int var = 20; /* actual variable declaration */
int *ip;
/* pointer variable declaration */
ip = &var; /* store address of var in pointer variable*/

printf("Address of var variable: %x\n", &var );


/* address stored in pointer variable */
printf("Address stored in ip variable: %x\n", ip );
/* access the value using the pointer */
printf("Value of *ip variable: %d\n", *ip );
return 0;
}
When the above code is compiled and executed, it produces result something as follows:
Address of var variable: bffd8b3c
Address stored in ip variable: bffd8b3c
Value of *ip variable: 20
NULL Pointers in C
It is always a good practice to assign a NULL value to a pointer variable in case you do not have exact
address to be assigned. This is done at the time of variable declaration. A pointer that is assigned NULL is
called a null pointer.
The NULL pointer is a constant with a value of zero defined in several standard libraries. Consider the
following program:
#include <stdio.h>
int main ()
{
int *ptr = NULL;
printf("The value of ptr is : %x\n", ptr );
return 0;
}
When the above code is compiled and executed, it produces the following result:
The value of ptr is 0
On most of the operating systems, programs are not permitted to access memory at address 0 because that
memory is reserved by the operating system. However, the memory address 0 has special significance; it
signals that the pointer is not intended to point to an accessible memory location. But by convention, if a
pointer contains the null (zero) value, it is assumed to point to nothing.
To check for a null pointer you can use an if statement as follows:
if(ptr) /* succeeds if p is not null */
if(!ptr) /* succeeds if p is null */

C Pointers in Detail:
Pointers have many but easy concepts and they are very important to C programming. There are following
few important pointer concepts which should be clear to a C programmer:
Concept

Description

C - Pointer arithmetic

There are four arithmetic operators that can be


used on pointers: ++, --, +, -

C - Array of pointers

You can define arrays to hold a number of


pointers.

C - Pointer to pointer

C allows you to have pointer on a pointer and so


on.

Passing pointers to functions in C

Passing an argument by reference or by address


both enable the passed argument to be changed
in the calling function by the called function.

C allows a function to return a pointer to local


Return pointer from functions in
variable, static variable and dynamically
C
allocated memory as well.
CLASSES AND OBJECTS
The main purpose of C++ programming is to add object orientation to the C programming language and
classes are the central feature of C++ that supports object-oriented programming and are often called userdefined types.
A class is used to specify the form of an object and it combines data representation and methods for
manipulating that data into one neat package. The data and functions within a class are called members of
the class.
C++ Class Definitions:
When you define a class, you define a blueprint for a data type. This doesn't actually define any data, but it
does define what the class name means, that is, what an object of the class will consist of and what
operations can be performed on such an object.
A class definition starts with the keyword class followed by the class name; and the class body, enclosed by
a pair of curly braces. A class definition must be followed either by a semicolon or a list of declarations. For
example, we defined the Box data type using the keyword class as follows:
class Box
{
public:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};

The keyword public determines the access attributes of the members of the class that follow it. A public
member can be accessed from outside the class anywhere within the scope of the class object. You can also
specify the members of a class as private or protected which we will discuss in a sub-section.
Define C++ Objects:
A class provides the blueprints for objects, so basically an object is created from a class. We declare objects
of a class with exactly the same sort of declaration that we declare variables of basic types. Following
statements declare two objects of class Box:
Box Box1;
Box Box2;

// Declare Box1 of type Box


// Declare Box2 of type Box

Both of the objects Box1 and Box2 will have their own copy of data members.
Accessing the Data Members:
The public data members of objects of a class can be accessed using the direct member access operator (.).
Let us try the following example to make the things clear:
#include <iostream>
using namespace std;
class Box
{
public:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
int main( )
{
Box Box1;
// Declare Box1 of type Box
Box Box2;
// Declare Box2 of type Box
double volume = 0.0; // Store the volume of a box here
// box 1 specification
Box1.height = 5.0;
Box1.length = 6.0;
Box1.breadth = 7.0;
// box 2 specification
Box2.height = 10.0;
Box2.length = 12.0;
Box2.breadth = 13.0;
// volume of box 1
volume = Box1.height * Box1.length * Box1.breadth;

cout << "Volume of Box1 : " << volume <<endl;


// volume of box 2
volume = Box2.height * Box2.length * Box2.breadth;
cout << "Volume of Box2 : " << volume <<endl;
return 0;
}
When the above code is compiled and executed, it produces the following result:
Volume of Box1 : 210
Volume of Box2 : 1560
It is important to note that private and protected members can not be accessed directly using direct member
access operator (.). We will learn how private and protected members can be accessed.
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.

C++ Object and Class


C++ Class
A class is the collection of related data and function under a single name. A C++ program can have
any number of classes. When related data and functions are kept under a class, it helps to visualize
the complex problem efficiently and effectively.

A Class is a blueprint for objects


When a class is defined, no memory is allocated. You can imagine like a datatype.
int var;
The above code specifies var is a variable of type integer; int is used for specifying variable var is
of integer type. Similarly, class are also just the specification for objects and object bears the
property of that class.

Defining the Class in C++


Class is defined in C++ programming using keyword class followed by identifier(name of class).
Body of class is defined inside curly brackets an terminated by semicolon at the end in similar way
as structure.
class class_name
{
// some data
// some functions
};
Example of Class in C++
class temp
{
private:
int data1;
float data2;
public:
void func1()
{ data1=2; }
float func2(){
data2=3.5;
retrun data;
}
};
Explanation
As mentioned, definition of class starts with keyword class followed by name of class( temp) in this
case. The body of that class is inside the curly brackets and terminated by semicolon at the end.
There are two keywords: private and public mentioned inside the body of class.

Keywords: private and public


Keyword private makes data and functions private and keyword public makes data and functions
public. Private data and functions are accessible inside that class only whereas, public data and
functions are accessible both inside and outside the class. This feature in OOP is known as data
hiding. If programmer mistakenly tries to access private data outside the class, compiler shows
error which prevents the misuse of data. Generally, data are private and functions are public.
C++ Objects
When class is defined, only specification for the object is defined. Object has same relationship to
class as variable has with the data type. Objects can be defined in similary way as structure is
defined.
Syntax to Define Object in C++
class_name variable name;
For the above defined class temp, objects for that class can be defined as:
temp obj1,obj2;
Here, two objects(obj1 and obj2) of temp class are defined.
Data member and Member functions
The data within the class is known as data member. The function defined within the class is known
as member function. These two technical terms are frequently used in explaining OOP. In the above
class temp, data1 and data2 are data members and func1() and func2() are member functions.
Accessing Data Members and Member functions
Data members and member functions can be accessed in similar way the member of structure is
accessed using member operator(.). For the class and object defined above, func1() for
object obj2can be called using code:
obj2.func1();
Similary, the data member can be accessed as:
object_name.data_memeber;
Note: You cannot access the data member of the above class temp because both data members are
private so it cannot be accessed outside that class.
Example to Explain Working of Object and Class in C++ Programming
/* Program to illustrate working of Objects and Class in C++ Programming */
#include <iostream>
using namespace std;
class temp
{

private:
int data1;
float data2;
public:
void int_data(int d){
data1=d;
cout<<"Number: "<<data1;
}
float float_data(){
cout<<"\nEnter data: ";
cin>>data2;
return data2;
}
};
int main(){
temp obj1, obj2;
obj1.int_data(12);
cout<<"You entered "<<obj2.float_data();
return 0;
}
Output:
Number: 12
Enter data: 12.43
You entered: 12.43
Explanation of Program
In
this
program,
two
data
members data1 and data2 and
two
member
function int_data() andfloat_data() are defined under temp class. Two objects obj1 and obj2 of that
class are declared. Function int_data() for the obj1 is executed using code obj1.int_data(12);, which
sets 12 to thedata1 of object obj1. Then, function float_data() for the object obj2 is executed which
takes data from user; stores it in data2 of obj2 and returns it to the calling function.
Note: In this program, data2 for object obj1 and data1 for object obj2 is not used and contains
garbage value.

Defining Member Function Outside the Class


A large program may contain many member functions. For the clarity of the code, member
functions can be defined outside the class. To do so, member function should be declared inside the
class(function prototype should be inside the class). Then, the function definition can be defined
using scope resolution operator ::. Learn more about defining member function outside the class.

Вам также может понравиться