Есть ли способ ссылаться на класс текущего объекта - PullRequest
1 голос
/ 08 апреля 2019

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

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 виртуальным и перезаписать его в производных классах, но было бы лучше просто написать его один раз и покончить с этим. Есть ли способ сделать это или что-то, что приводит к тому же самому?

Ответы [ 3 ]

0 голосов
/ 08 апреля 2019

Вы смотрели на использование виртуального 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 работает, вполне можно использовать это,Тем не менее, это проще, чем использовать шаблоны или пользовательские классы функторов.Лямбда - разумная альтернатива, с преимуществом не меняя интерфейс вашего класса (заголовочный файл).

0 голосов
/ 08 апреля 2019

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

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

0 голосов
/ 08 апреля 2019

В случае, если ваш 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)

  • C ++ 11:

    void StartThreads()
    {
        std::thread fooThread([=](){ this->FooFunc(); });
        fooThread.join();
    }
    
  • C ++ 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();
    }
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...