Эффективность строковых ссылочных параметров в C ++ - PullRequest
0 голосов
/ 16 июня 2020

В C ++, когда аргументы являются строками, есть ли какая-либо заметная скорость / эффективность, полученная за счет их ссылок на строковые объекты, как это делается, когда аргументы являются объектами класса, созданного программистом? Например, если бы у меня был класс под названием Person, у которого были имя и фамилия, я мог бы сделать для одного из конструкторов:

Person(const string& firstName, const string& lastName);

vs.

Person(string firstName, string lastName);

Первый еще один эффективный, или полученная эффективность настолько тривиальна, что не имеет значения?

Ответы [ 3 ]

1 голос
/ 16 июня 2020

Вместо этих двух подходов

Person(const string& firstName, const string& lastName);

и

Person(string firstName, string lastName);

более эффективный подход - это когда у вас есть вместе следующие два конструктора

Person(const string &firstName, const string &lastName);
Person( string &&firstName, string &&lastName );

Это Подход

Person(const string& firstName, const string& lastName);

менее эффективен, когда аргументы являются r-значениями.

Этот подход

Person(string firstName, string lastName);

менее эффективен, когда аргументы имеют l-значения.

Вот две демонстрационные программы, которые показывают различия между тремя подходами.

#include <iostream>

struct A
{
    static int value;

    explicit A() : x( ++value )
    {
        std::cout << "explicit A(), x = " << x << "\n";
    }

    A( const A &a ) noexcept : x( ++value )
    {
        std::cout << "A( const A & ), x = " << x << "\n";
    }

    A( A &&a ) noexcept : x( ++value )
    {
        std::cout << "A( A && ), x = " << x << "\n";
    }

    ~A()
    {
        std::cout << "~A(), x = " << x << "\n";
    }

    int x;
};

int A::value = 0;

struct B
{
    B( const A &a1, const A &a2 ) : a1( a1 ), a2( a2 )
    {
        std::cout << "B( const A &, const A & )\n";
    }
    A a1, a2;
};

struct C
{
    C( A a1, A a2 ) : a1( std::move( a1 ) ), a2( std::move( a2 ) )
    {
        std::cout << "C( A, A )\n";
    }

    A a1, a2;
};

struct D
{

    D( const A &a1, const A &a2 ) : a1( a1 ), a2( a2 )
    {
        std::cout << "D( const A &, const A & )\n";
    }

    D( A &&a1, A &&a2 ) : a1( std::move( a1 ) ), a2( std::move( a2 ) )
    {
        std::cout << "D( A &&, A && )\n";
    }

    A a1, a2;
};

int main()
{
    A a1;
    A a2;

    std::cout << '\n';

    B b( a1, a2 );

    std::cout << "b.a1.x = " << b.a1.x << ", b.a2.x = " << b.a2.x << '\n';

    std::cout << '\n';

    C c( a1, a2 );

    std::cout << "c.a1.x = " << c.a1.x << ", c.a2.x = " << c.a2.x << '\n';

    std::cout << '\n';

    D d( a1, a2 );

    std::cout << "d.a1.x = " << d.a1.x << ", d.a2.x = " << d.a2.x << '\n';

    std::cout << '\n';

    return 0;
}

Вывод программы:

explicit A(), x = 1
explicit A(), x = 2

A( const A & ), x = 3
A( const A & ), x = 4
B( const A &, const A & )
b.a1.x = 3, b.a2.x = 4

A( const A & ), x = 5
A( const A & ), x = 6
A( A && ), x = 7
A( A && ), x = 8
C( A, A )
~A(), x = 6
~A(), x = 5
c.a1.x = 7, c.a2.x = 8

A( const A & ), x = 9
A( const A & ), x = 10
D( const A &, const A & )
d.a1.x = 9, d.a2.x = 10

~A(), x = 10
~A(), x = 9
~A(), x = 8
~A(), x = 7
~A(), x = 4
~A(), x = 3
~A(), x = 2
~A(), x = 1

И

#include <iostream>

struct A
{
    static int value;

    explicit A() : x( ++value )
    {
        std::cout << "explicit A(), x = " << x << "\n";
    }

    A( const A &a ) noexcept : x( ++value )
    {
        std::cout << "A( const A & ), x = " << x << "\n";
    }

    A( A &&a ) noexcept : x( ++value )
    {
        std::cout << "A( A && ), x = " << x << "\n";
    }

    ~A()
    {
        std::cout << "~A(), x = " << x << "\n";
    }

    int x;
};

int A::value = 0;

struct B
{
    B( const A &a1, const A &a2 ) : a1( a1 ), a2( a2 )
    {
        std::cout << "B( const A &, const A & )\n";
    }
    A a1, a2;
};

struct C
{
    C( A a1, A a2 ) : a1( std::move( a1 ) ), a2( std::move( a2 ) )
    {
        std::cout << "C( A, A )\n";
    }

    A a1, a2;
};

struct D
{

    D( const A &a1, const A &a2 ) : a1( a1 ), a2( a2 )
    {
        std::cout << "D( const A &, const A & )\n";
    }

    D( A &&a1, A &&a2 ) : a1( std::move( a1 ) ), a2( std::move( a2 ) )
    {
        std::cout << "D( A &&, A && )\n";
    }

    A a1, a2;
};

int main()
{
    B b( A{}, A{} );

    std::cout << "b.a1.x = " << b.a1.x << ", b.a2.x = " << b.a2.x << '\n';

    std::cout << '\n';

    C c( A{}, A{} );

    std::cout << "c.a1.x = " << c.a1.x << ", c.a2.x = " << c.a2.x << '\n';

    std::cout << '\n';

    D d( A{}, A{} );

    std::cout << "d.a1.x = " << d.a1.x << ", d.a2.x = " << d.a2.x << '\n';

    std::cout << '\n';

    return 0;
}

Вывод программы:

explicit A(), x = 1
explicit A(), x = 2
A( const A & ), x = 3
A( const A & ), x = 4
B( const A &, const A & )
~A(), x = 2
~A(), x = 1
b.a1.x = 3, b.a2.x = 4

explicit A(), x = 5
explicit A(), x = 6
A( A && ), x = 7
A( A && ), x = 8
C( A, A )
~A(), x = 6
~A(), x = 5
c.a1.x = 7, c.a2.x = 8

explicit A(), x = 9
explicit A(), x = 10
A( A && ), x = 11
A( A && ), x = 12
D( A &&, A && )
~A(), x = 10
~A(), x = 9
d.a1.x = 11, d.a2.x = 12

~A(), x = 12
~A(), x = 11
~A(), x = 8
~A(), x = 7
~A(), x = 4
~A(), x = 3

Видно, что в обеих программах класс D ведет себя более эффективно по сравнению с классами B и C.

1 голос
/ 16 июня 2020

Первый более эффективный,

Может быть, потенциально. Но не обязательно.

или полученная эффективность настолько тривиальна, что не имеет значения?

Это зависит от многих вещей. Если строки короткие (достаточно, чтобы соответствовать небольшой оптимизации строки целевой системы), тогда разница, вероятно, будет небольшой в любом случае.

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

Если вы собираетесь сохранить копию и передаете rvalue функции, а строка большая, то ссылка будет вероятно, будет значительно быстрее, потому что вы можете создать копию путем перемещения.

0 голосов
/ 16 июня 2020

Что ж, вы заставили меня вспомнить хорошее эмпирическое правило, которое однажды меня научил один из моих профессоров.

Когда вам нужно решить, передавать ли объект в функцию по значению или по ссылке: если объект больше, чем ссылка на него, до 4x - мы передадим сам объект, если он больше 4x - тогда передадим ссылку на него.

Конечно, это сделано в предположении, что в любом случае мы не планируем изменять объект.

...