Форум программистов, компьютерный форум, киберфорум
C++
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.93/15: Рейтинг темы: голосов - 15, средняя оценка - 4.93
0 / 0 / 0
Регистрация: 28.08.2016
Сообщений: 16

Передача указателя на функцию-член

29.08.2016, 00:05. Показов 3888. Ответов 41
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Всем привет !

Необходимо передать функцию-член в качестве параметра другой функции-члену. Следующий код

C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Menu {
    public:
    void spin();   
};
void Menu::spin() {
  ;
}
class Encoder {
  public:
    void setSpinFunc(void(Menu::*i)());
};
void Encoder::setSpinFunc(void(Menu::*i)()) {
  ;
}
int main()
{
    Menu m();
    Encoder e();
    e.setSpinFunc(m.spin);
}
вызывает ошибку: request for member 'setSpinFunc' in 'e', which is of non-class type 'Encoder()'

Уже долго туплю над этим, похоже сам не въеду, разъясните что не так.
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
29.08.2016, 00:05
Ответы с готовыми решениями:

Передача типа указателя на функцию
Всем привет. Недавно в теме начинающих возник вопрос, а можно ли передать тип (указатель на функцию) без использования typedef: ...

Передача указателя на CALLBACK функцию
делаю класс приложения для более быстрого создания программ и просто инкапсуляции всего и вся)) и вот я сделал класс cApplication, один...

Передача указателя на функцию-член класса
Необходимо передать фунцию-член класса как аргумент в функцию другого класса. Код: Файл main.cpp #include "head.hpp" ...

41
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
29.08.2016, 03:04
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Menu {
    public:
    void spin();   
};
void Menu::spin() {
  ;
}
class Encoder {
  public:
    void setSpinFunc(void(Menu::*)());
};
void Encoder::setSpinFunc(void(Menu::*i)()) {
  ;
}
int main()
{
    Menu m;
    Encoder e;
    e.setSpinFunc(&Menu::spin);
}
Добавлено через 3 минуты
это объявления прототипов функций.
Цитата Сообщение от Inversus Посмотреть сообщение
Menu m();
Encoder e();
вот здесь:
Цитата Сообщение от Inversus Посмотреть сообщение
request for member 'setSpinFunc' in 'e', which is of non-class type 'Encoder()'
компилятор как бы в недоумении.

у прототипов функций нельзя вызывать функции-члены.
это же не объекты классов.
1
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
29.08.2016, 06:02
Цитата Сообщение от Inversus Посмотреть сообщение
Необходимо передать функцию-член в качестве параметра другой функции-члену.
Так определись сразу - функцию член класса или функцию-член объекта. Это две огромные разницы.
0
0 / 0 / 0
Регистрация: 28.08.2016
Сообщений: 16
29.08.2016, 10:02  [ТС]
Становится все интереснее)

это объявления прототипов функций.
Действительно, читается двояко. Хотя Я привык что прототипы пишутся перед main() - странно что вылазит именно такая ошибка. На самом деле у этих классов есть конструкторы с параметрами, здесь Я их просто убрал для упрощения.

В общем код
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Menu {
    public:
    void spin();   
};
void Menu::spin() {
  ;
}
class Encoder {
  public:
    void setSpinFunc(void(Menu::*)());
};
void Encoder::setSpinFunc(void(Menu::*i)()) {
  ;
}
int main()
{
    Menu m;
    Encoder e;
    e.setSpinFunc(&Menu::spin);
}
действительно прокатывает, но как уточнил Fulcrum_013 - полагаю что мне нужно передавать именно функцию-член объекта.

Вот это (добавил конструкторы)

C++
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
35
36
37
38
39
class Menu {
    public:
    Menu::Menu(char* str)
    void spin();   
};
 
Menu::Menu(char* str) {
   ;
}
 
void Menu::spin() {
   ;
}
 
 
class Encoder {
  void (Menu::*spin)();
 
  public:
    Encoder(char a, char b, int p);
    void setSpinFunc(void(Menu::*i)());
};
 
Encoder::Encoder(char a, char b, int p) {
    ;
}
 
void Encoder::setSpinFunc(void(Menu::*i)()) {
  spin = i;
}
 
 
int main()
{
    Menu m("a1|a2|a3");
 
    Encoder e(1,2,3);
    e.setSpinFunc(m.spin);
}
-прокатывает в одном компиляторе и не прокатывает в другом -
no matching function for call to 'Encoder<Menu>::setSpinFunc(<unresolved overloaded function type>)'

Т.е. идея следующая - создается экземпляр Menu и экземпляр Encoder, в Encoder запоминается указатель на spin, и затем из некоторой функции-члена Encoder необходимо периодически вызывать spin. А в каких случаях может понадобиться передавать функцию-член класса ? Или можно и через класс и через объект ? Fulcrum_013, не затруднит дать развернутый ответ с примерами и с вызовом spin из Encoder ?
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
29.08.2016, 11:44
Цитата Сообщение от Inversus Посмотреть сообщение
-прокатывает в одном компиляторе и не прокатывает в другом -
остановок "здеся" и "тута" не существует.

Цитата Сообщение от Inversus Посмотреть сообщение
действительно прокатывает, но как уточнил Fulcrum_013 - полагаю что мне нужно передавать именно функцию-член объекта.
он несет бред.

понятия "функция-член объекта" на плюсах не существует.

Цитата Сообщение от Inversus Посмотреть сообщение
А в каких случаях может понадобиться передавать функцию-член класса ?
http://rextester.com/FWEAP62520


C++
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
#include <iostream>
 
struct Menu 
{
    void spin(){ std::cout << "Menu::spin\n"; }
};
 
struct Encoder 
{
  void (Menu::*spin)();
  Menu* menu;
 
    void setSpinFunc(void(Menu::*i)(), Menu& m)
    {
        menu = &m;
        spin = i;
    }
    
    void execute()
    {
        (menu->*spin)();
    }
};
 
 
 
int main()
{
    Menu m;
    Encoder e;
    e.setSpinFunc(&Menu::spin, m);
    e.execute();
}
1
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
29.08.2016, 19:51
Цитата Сообщение от hoggy Посмотреть сообщение
понятия "функция-член объекта" на плюсах не существует
Поэтому и приходится наворачивать костыли для реализации делегата. Хотя разница лишь в добавлении к указателю на метод указателя на объект

Добавлено через 3 минуты
Цитата Сообщение от hoggy Посмотреть сообщение
он несет бред.
То вы бред несете. Если коммитет проглядел стандартизацию назревших 20 лет вещей это не значит что их не существует или в них не существует необходимости.

Добавлено через 14 минут
Цитата Сообщение от Inversus Посмотреть сообщение
полагаю что мне нужно передавать именно функцию-член объекта.
если нужно передовать указатель на член объекта то обычно делают так чтобы во всех компиляторах работало:
C++
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
typedef RetType (*HandlerProc)(Args);
class B:public BaseClass{
      public: 
             RetType Handler(Args){std::cout << "Delegate called"};
}
class A{
private:
      std::function<HandlerProc> Handler;
public:
       void SetHandler(std::function<HandlerProc> NewHandler){
                          Handler=NewHandler;
       };
       void Process(){
              //там где надо вызвать хандлер   
              if (Handler!=nullptr) Handler(Args);
              
       }
}
void main(){
       A a;
       B b;
       // инициализация делегата
      a.SetHandler([b](Args)->RetType{return b.Handler(Args;)});
      a.Process(); 
}
в общем с таким костылем через std::function и лямбду получается очень многословная и неудобная инициализация делегата.

Добавлено через 14 минут
Такая конструкция нужна когда A ничего не знает о B и/или может иметь несколько хандлеров в каждом из которых пользуются разные экземпляры B
1
Эксперт С++
 Аватар для Avazart
8488 / 6155 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
29.08.2016, 20:49
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
То вы бред несете. Если коммитет проглядел стандартизацию назревших 20 лет вещей это не значит что их не существует или в них не существует необходимости.
Кому не нужно использует давно Qt или boost::signals2 и не закатывает истерик.
0
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
29.08.2016, 21:03
Цитата Сообщение от Avazart Посмотреть сообщение
Кому не нужно использует давно Qt или boost::signals2 и не закатывает истерик.
Потому как в Qt имеется механизм делегатов которого нет в стандарте? Так такие механизмы есть в многих местах кроме стандарта, а соответственно и реализовываются несовместимо друг с другом. Не пора бы их стандартизировать?
0
0 / 0 / 0
Регистрация: 28.08.2016
Сообщений: 16
29.08.2016, 21:34  [ТС]
Японамать, мой чайник закипел

hoggy, спасибо, теперь буду знать что в стуктуры можно пихать функции

Т.е. что-бы вызвать функцию-член берем указатель на класс и добавляем смещение на функцию-член класса. У меня после JavaScript вообще теперь каша в голове

Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
если нужно передавать указатель на член объекта то обычно делают так чтобы во всех компиляторах работало:
Спасибо, буду втыкать )

Цитата Сообщение от Avazart Посмотреть сообщение
использует давно Qt
Сам в нем пишу что можно, жаль только под Arduino не адаптирован.
0
Эксперт С++
 Аватар для Avazart
8488 / 6155 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
29.08.2016, 21:41
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Не пора бы их стандартизировать?
Понту говорить о том чего нет... и на вряд ли будет...
0
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
29.08.2016, 21:43
Цитата Сообщение от Inversus Посмотреть сообщение
теперь буду знать что в стуктуры можно пихать функции
Их еще и наследовать можно. По большому счету теперь структуры отличаются от классов только тем что у структур по умолчанию public видимость и public наследование.

Добавлено через 1 минуту
Цитата Сообщение от Avazart Посмотреть сообщение
Понту говорить о том чего нет... и на вряд ли будет...
Ну да... эпоха визуальной разработки а необходимые для этого средства коммитет стандартизировать как понимаю не собирается.. КОММИТЕТ АКБАР!!!
0
Эксперт С++
 Аватар для Avazart
8488 / 6155 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
29.08.2016, 21:45
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
эпоха визуальной разработки
Давно прошла... В месте с популярностью Delphi где-то.
0
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
29.08.2016, 21:47
Цитата Сообщение от Avazart Посмотреть сообщение
Давно прошла...
угу. Qt тоже средство визуальной разработки. И MSVC вроде бы как таковое даже по названию позиционируется. И чем дальше тем больше задачи становятся обычно задачами разработки средств визуальной разработки для нужд конечного пользователя. А эпоха дельфы вернее Билдера по большому счету еще не наступила.
Вообще концепция КОП заключается в том что непосредственно бизнес-логику должны визуально собирать менеджеры из настриваемых визуальных блоков которые изготавливают программисты.
Кстати такой подход не только в баксосчитании пользуется. К примеру Simantec Step 7 тоже на этой идеологии держится как и многие SCADA-системы.
1
Эксперт С++
 Аватар для Avazart
8488 / 6155 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
29.08.2016, 21:56
MSVC продвигает давно C# у которого "включено".

Добавлено через 4 минуты
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
И чем дальше тем больше задачи становятся обычно задачами разработки средств визуальной разработки для нужд конечного пользователя.
Очнись, некоторые С++ разработчики даже GUI не касаются, пишут себе сервера и консольки....
А учитывая что единого стандартизированного GUI (а значит кроссплатформенного) нет в С++ то и этот вопрос не настолько актуален.

Добавлено через 3 минуты
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
А эпоха дельфы вернее Билдера по большому счету еще не наступила.
Что б тебя динозавры съели )) В какая это эпоха... Они фиг знает сколько готовый компилятор пилили с начало под одну платформу(x64) потом под другую... потом вылезли динамические зависимости.... бока... бока... и их они по два три года не исправляют... Все спехом все через ...
0
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
29.08.2016, 23:00
Цитата Сообщение от Avazart Посмотреть сообщение
Очнись, некоторые С++ разработчики даже GUI не касаются, пишут себе сервера и консольки....
Это не значит что при обработке данных сервом и консолькой не нужны делегаты или не возможно преимущественно визуальное проектирование ливера этих сервов.

Добавлено через 8 минут
Цитата Сообщение от Avazart Посмотреть сообщение
Очнись, некоторые С++ разработчики даже GUI не касаются, пишут себе сервера и консольки....
И даже внутренности контроллеров с использованием визуальной разработки.
Цитата Сообщение от Avazart Посмотреть сообщение
А учитывая что единого стандартизированного GUI (а значит кроссплатформенного) нет в С++ то и этот вопрос не настолько актуален.
Единого нет. Но для визуальной разработки каким бы оно ни было (причем не только GUI) нужны единые языковые инструменты. А именно свойства делегаты и RTTI в объеме который называют reflection. При этом reflection и свойства нужны еще и для полиморфной сериализации/десериализации объектов, а где пользовать делегаты мест тоже найдется куча и не касаемо GUI. Все таки на дворе уже 40 лет как эпоха событийного управления. Причем реализация нативной поддержки всего этого в компиляторах будет гораздо менее сложной чем те дела которые непонятно зачем комитет пихает в стандарт и не будут приводить к ограничению в механизмах используемых в программах (к примеру в отличии от автоматического рефкаунтинга который непонятно с какой целью сунули в 17-ый стандарт)

Добавлено через 16 минут
Цитата Сообщение от Avazart Посмотреть сообщение
Они фиг знает сколько готовый компилятор пилили с начало под одну платформу(x64) потом под другую... потом вылезли динамические зависимости....
Хорошие вещи быстро не делаются. И без боков не делаются. Боков не бывает только у тех кто вообще ничего не делает. К примеру "Союз" тоже далеко не с первого запуска полетел. Так же как и Falcon-9. А к примеру "Бураны" тоже не летают потому что их эпоха еще не наступила (хотя эпоха внешне очень похожих шаттлов уже закончилась). При этом к примеру бортовой софт "Бурана" и "Энергии" на 100% на событийном управлении и на 100% визуальной разработки (правда не ООП)

Добавлено через 30 минут
Цитата Сообщение от Avazart Посмотреть сообщение
некоторые С++ разработчики даже GUI не касаются
К примеру TDataModule в С++ Builder тоже GUI не касается. Но тем не менее компоненты которые в нем размещаются тоже связываются и настраиваются преимущественно визуально.
0
Эксперт С++
 Аватар для Avazart
8488 / 6155 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
29.08.2016, 23:03
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Хорошие вещи быстро не делаются. И без боков не делаются.
Ну явно не пятилетками, это все же IT - либо вовремя либо никогда.

Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Это не значит что при обработке данных сервом и консолькой не нужны делегаты или не возможно преимущественно визуальное проектирование ливера этих сервов.
Например, зачем это нужно в коде сервера или консольке?

Добавлено через 3 минуты
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
К примеру TDataModule в С++ Builder тоже GUI не касается. Но тем не менее компоненты которые в нем размещаются тоже связываются и настраиваются преимущественно визуально.
Нарушая при этом инкапсуляцию. Это как раз беда билдера.
Для работы с БД это еще более или менее...
Но для работы к примеру в потоке все равно придется создавать и настраивать компоненты в коде.
0
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
29.08.2016, 23:06
Цитата Сообщение от Avazart Посмотреть сообщение
Например, зачем это нужно в коде сервера?
Пример - на сокет пришла информация. Необходимо оповестить обрабатывающий контекст. Сокет отправил данные. Тоже надо контекст оповестить. Коннект опять же нужно контекст оповестить. Дисконнет то же самое. Не кажется ли что все это внутри одного процесса удобнее делать через делегата чем через мессаджи? А даже если и через мессаджи то подписку на получение события из системы диспетчеризации сообщений тоже удобно через делегаты делать. Ну и так вобщем на каждом шагу. Чем больше взаимосвязей между объектами информационой модели тем от большего головняка при проектировании иерархии избавляют делегаты.
0
Эксперт С++
 Аватар для Avazart
8488 / 6155 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
29.08.2016, 23:09
А вы гляньте в boost/asio. Я не вижу такую необходимости.
0
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
29.08.2016, 23:18
Цитата Сообщение от Avazart Посмотреть сообщение
Нарушая при этом инкапсуляцию.
Что конкретно подразумевается под нарушением инкапсуляции? Да кстати очень часто нарушение инкапсуляции дает абсолютную инкапсуляцию на более высоком уровне. (Сокрытие и обеспечение невмешательства извне в механизм взаимодействия объектов двух или нескольких классов)
Как на меня нарушение инкапсуляции это к примеру публичные а не протектед геттеры и сеттеры.

Добавлено через 4 минуты
Цитата Сообщение от Avazart Посмотреть сообщение
Я не вижу такую необходимости
Если контексты-обработчики объекты то необходимость такая есть как бы механизм ее реализации не был организован. К примеру можно костылить через std и лямбды, можно через статик методы - посредники и передачу багажом контекста, а можно через нативную поддержку делегата который в принципе делает то же самое только с гораздо более удобным синтаксисом и гораздо меньшим количеством кода.
0
Эксперт С++
 Аватар для Avazart
8488 / 6155 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
29.08.2016, 23:20
К примеру что мешает сделать так:
C++
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
void AbstractMenu
{
    public:
        virtual void spin()=0;   
};
 
 
class Menu: public AbstractMenu
{
    public:
    virtual void spin();   
};
 
 
void Menu::spin() 
{
};
 
 
class Encoder {
  public:
    void setSpinFunc(AbstractMenu* menu);
};
void Encoder::setSpinFunc(AbstractMenu* menu) 
{
     menu->spin();
}
int main()
{
    Menu m();
    Encoder e();
    e.setSpinFunc(&m);
}
Добавлено через 1 минуту
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Что конкретно подразумевается под нарушением инкапсуляции?
Компоненты видны "снаружи" модуля, так как находятся в секции __published
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
29.08.2016, 23:20
Помогаю со студенческими работами здесь

Хранение в map указателя на функцию-член шаблонного класса
Здравствуйте! Сделал я себе вызов написать джунгли из ООП деревьев, типо, объекты обмениваются между собой сообщениями используя их...

Передача указателя на шаблонную функцию в другую функцию
Пишу тест для нескольких улучшений квиксорта с измерением времени. Функция benchmark принимает итераторы для диапазона элементов, указатель...

Передача указателя в функцию
Поиском не пользовался, возможно данный вопрос уже проскакивал. Проблема том, что VS2010 на отрез отказывается передавать указатель на...

Передача указателя на функцию
Доброго дня Форумчане. Хотелось бы узнать как вызывать функцию получив на нее указатель? пример class C1 { public: void...

Передача в функцию указателя
Почему плохо передавать в функцию указатель на строку или массив и возвращать указатель на строку или массив?


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
делаю науч статью по влиянию грибов на сукцессию
anaschu 13.03.2026
прикрепляю статью
SDL3 для Desktop (MinGW): Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
8Observer8 10.03.2026
Содержание блога Финальные проекты на Си и на C++: hello-sdl3-c. zip hello-sdl3-cpp. zip Результат:
Установка CMake и MinGW 13.1 для сборки С и C++ приложений из консоли и из Qt Creator в EXE
8Observer8 10.03.2026
Содержание блога MinGW - это коллекция инструментов для сборки приложений в EXE. CMake - это система сборки приложений. Здесь описаны базовые шаги для старта программирования с помощью CMake и. . .
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд. Даже если у вас. . .
Модульная разработка через nuget packages
DevAlt 07.03.2026
Сложившийся в . Net-среде способ разработки чаще всего предполагает монорепозиторий в котором находятся все исходники. При создании нового решения, мы просто добавляем нужные проекты и имеем. . .
Модульный подход на примере F#
DevAlt 06.03.2026
В блоге дяди Боба наткнулся на такое определение: В этой книге («Подход, основанный на вариантах использования») Ивар утверждает, что архитектура программного обеспечения — это структуры,. . .
Управление камерой с помощью скрипта OrbitControls.js на Three.js: Вращение, зум и панорамирование
8Observer8 05.03.2026
Содержание блога Финальная демка в браузере работает на Desktop и мобильных браузерах. Итоговый код: orbit-controls-threejs-js. zip. Сканируйте QR-код на мобильном. Вращайте камеру одним пальцем,. . .
SDL3 для Web (WebAssembly): Синхронизация спрайтов SDL3 и тел Box2D
8Observer8 04.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-sync-physics-sprites-sdl3-c. zip На первой гифке отладочные линии отключены, а на второй включены:. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru