【问题标题】:Efficiency of string reference parameters in C++C++中字符串引用参数的效率
【发布时间】:2020-10-06 01:14:41
【问题描述】:

在 C++ 中,当参数是字符串时,是否像当参数是程序员创建的类的对象时一样,通过引用字符串对象来获得明显的速度/效率?例如,如果我有一个名为 Person 的类,它有名字和姓氏,我可以为其中一个构造函数做:

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

对比

Person(string firstName, string lastName);

第一个效率更高,还是获得的效率如此微不足道而无关紧要?

【问题讨论】:

  • 或者获得的效率如此微不足道,这无关紧要?如果您正在寻找可衡量的差异,它可能取决于数百该函数将被调用的次数。
  • 显然,第一个更有效。传递对象的地址比复制它的工作量少。
  • 标准库类和自己创建的类没有区别。
  • 一个(一段)字符串有多长?效率的提高取决于字符串的长度。

标签: c++ string performance reference


【解决方案1】:

第一个效率更高,

有可能,有可能。但不一定。

还是说获得的效率如此微不足道而无关紧要?

这取决于很多事情。如果字符串很短(足以适应目标系统的小字符串优化),那么任何一种方式的差异都可能很小。

如果您不打算存储字符串的副本,并且字符串很大,那么引用可能会明显更快。

如果您要存储一个副本,并将一个右值传递给函数,并且字符串很大,那么非引用可能会明显更快,因为您可以通过移动来创建副本。


无论这些替代方案之间的差异在您的程序中是否显着,都只能通过测量来发现。

【讨论】:

  • 由于这些是名称,因此大小可能很小。
  • @drescherjm 但是not necessarily :)
【解决方案2】:

而不是这两种方法

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);

当参数是右值时效率较低。

这种方法

Person(string firstName, string lastName);

当参数是左值时效率较低。

这里有两个演示程序,显示了三种方法之间的差异。

#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

可以看出,与BC 相比,在这两个程序中,D 类的行为效率更高。

【讨论】:

  • 谢谢弗拉德。我不熟悉您描述的具有“字符串&&firstName”的更有效方法。我以前没有用过“&&”。你能解释一下吗?
  • @MikeWeston 这是可能绑定到临时对象的rvlaue引用。
【解决方案3】:

嗯, 你让我想起了我的一位教授曾经教过我的一条很好的经验法则。

当你必须决定是否将对象传递给函数时 按值或按引用:如果对象大于对它的引用, 高达 4x - 我们将传递对象本身, 如果它大于 4x - 那么我们将传递对它的引用。

当然,这是假设我们不打算以任何方式更改对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多