Есть ли способ сослаться на класс текущего объекта

Я создаю класс, в котором есть метод, который запускает несколько потоков функций-членов в том же классе. Я новичок в потоках на С++, особенно когда задействованы классы, но это то, что я придумал.

class A
{
public:
    void StartThreads()
    {
        std::thread fooThread(&A::FooFunc, this);
        fooThread.join();
    }
protected:
    virtual void FooFunc()
    {
        while (true)
            std::cout << "hello\n";
    }
};

Мой вопрос в том, могу ли я получить имя текущего объекта, потому что теперь, если я создам класс B, который наследуется от A, но перезаписывает FooFunc, FooFunc из класса A будет вызываться, когда я это делаю:

B b;
b.StartThreads();

Поэтому я ищу способ заменить std::thread fooThread(&A::FooFunc, this) на что-то вроде std::thread fooThread(&this->GetClass()::FooFunc, this). Я мог бы просто сделать StartThreads виртуальным и перезаписать его в производных классах, но было бы лучше просто написать его один раз и покончить с этим. Есть ли способ сделать это или что-то, что приводит к тому же самому?

Разве этот код уже не делает то, что вы хотите? он вызывает производный, если вы вызываете StartThreads в производном классе

Borgleader 08.04.2019 00:13
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
227
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

В случае, если ваш this известен во время компиляции, вам поможет статическое метапрограммирование.

C++, Swift и Rust (а теперь еще и Scala) — это статические языки, в которых есть много трюков во время компиляции для решения подобных проблем.

Как? В вашем случае шаблоны может вам помочь.

Кроме того, вам не нужно, чтобы это была функция-член, это может быть функция друга (чтобы вы могли легко использовать шаблоны).

class A
{
public:
    template<typename T>
    friend void StartThreads(const T& obj);
protected:
    virtual void FooFunc()
    {
        while (true)
            std::cout << "hello\n";
    }
};

template<typename T>
void StartThreads(const T& obj) {
    std::thread fooThread(&T::FooFunc, obj);
    fooThread.join();
}

ПРЕДУПРЕЖДЕНИЕ: Этот ТОЛЬКО работает, если класс известен во время компиляции, т.е.

class B: public A {
};

...

B b;
A &a = b;
StartThreads(a);  // Will call it AS IF IT IS A, NOT B

Другое решение:

Функциональное программирование на помощь, вы можете использовать лямбда-выражения (или функторы, использующие структуры, если вы используете C++ до C++11)

  • С++ 11:

    void StartThreads()
    {
        std::thread fooThread([=](){ this->FooFunc(); });
        fooThread.join();
    }
    
  • С++ 98:

    // Forward declaration     
    class A;
    
    // The functor class (the functor is an object that is callable (i.e. has the operator (), which is the call operator overloaded))
    struct ThreadContainer {
    private:
        A &f;
    public:
        ThreadContainer(A &f): f(f) {}
        void operator() ();
    };
    
    class A
    {
    public:
        // To allow access of the protected FooFunc function
        friend void ThreadContainer::operator() ();
    
        void StartThreads()
        {
            // Create the functor
            ThreadContainer a(*this);
            // Start the thread with the "call" operator, the implementation of the constructor tries to "call" the operand, which here is a
            std::thread fooThread(a);
            fooThread.join();
        }
    protected:
        virtual void FooFunc()
        {
            while (true)
                std::cout << "hello\n";
        }
    };
    
    class B: public A {
        protected:
        virtual void FooFunc() {
            while(true)
                std::cout << "overridden\n";
        }
    };
    
    void ThreadContainer::operator() () {
        f.FooFunc();
    }
    

Вы рассматривали использование виртуального FooFunc() напрямую и каким-то образом предположили, что это не работает. (Я не буду говорить о точности этого здесь, поскольку это поднимается в комментариях к вопросу.) Вам не нравится идея перемещения виртуальной функции на более раннем этапе процесса. Так почему бы не перенести его позже? Существует довольно распространенная парадигма, в которой используются невиртуальные оболочки для виртуальных функций. (Обычно оболочка общедоступна, а виртуальная функция защищена или закрыта.) Итак, что-то вроде:

class A
{
public:
    void StartThreads()
    {
        std::thread fooThread(&A::FooFuncCaller, this); // <-- call the new function
        fooThread.join();
    }
protected:
    void FooFuncCaller()  // <-- new function layer
    {
        FooFunc();
    }
    virtual void FooFunc()
    {
        while (true)
            std::cout << "hello\n";
    }
};

Конечно, если прямой вызов виртуального Foofunc работает, можно использовать и его. Тем не менее, это проще, чем использование шаблонов или пользовательских классов функторов. Лямбда - это разумная альтернатива, преимущество которой заключается в том, что вы не меняете интерфейс вашего класса (заголовочный файл).

Ответ принят как подходящий

Спасибо за все ваши ответы, оказалось, что мой вопрос не имел отношения к делу и что я запутал некоторых других участников в классе.

Спасибо за ваши ответы, которые дали мне некоторое представление о других способах, которыми вы можете сделать то же самое, используя разные методы. (https://stackoverflow.com/users/9335240/user9335240)

Другие вопросы по теме