class
friend
13.16 - 匿名对象
在某些情况下,我们只需要一个临时的变量。例如,考虑以下情况:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 #include <iostream>
int add ( int x , int y )
{
int sum { x + y };
return sum ;
}
int main ()
{
std :: cout << add ( 5 , 3 ) << '\n' ;
return 0 ;
}
在add()
函数中,变量 sum
只被当做一个临时的占位符变量来使用。它并没有其他特别的功能——它唯一的功能就是保存表达式的结果并作为返回值返回。
实际上,我们可以通过匿名对象编写一个更加简单的 add()
函数。匿名对象 本质上是一个没有名称的值。因为它们没有名字,所以在它们被创建后,我们没有办法去引用它们。因此,匿名对象只能具有“表达式作用域”,即它们在一个表达式中被创建、求值和销毁。
下面例子中的 add()
函数使用了匿名对象:
1
2
3
4
5
6
7
8
9
10
11
12
13 #include <iostream>
int add ( int x , int y )
{
return x + y ; // an anonymous object is created to hold and return the result of x + y
}
int main ()
{
std :: cout << add ( 5 , 3 ) << '\n' ;
return 0 ;
}
当表达式 x + y
求值时,它的结果会被存放到一个匿名对象中。然后,该对象的一个拷贝会被返回给主调函数,然后这个匿名对象就会被销毁。
这不仅适用于返回值,也适用于函数形参。例如,与其这样做:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 #include <iostream>
void printValue ( int value )
{
std :: cout << value ;
}
int main ()
{
int sum { 5 + 3 };
printValue ( sum );
return 0 ;
}
不如这样做:
1
2
3
4
5
6
7
8
9
10
11
12
13 #include <iostream>
void printValue ( int value )
{
std :: cout << value ;
}
int main ()
{
printValue ( 5 + 3 );
return 0 ;
}
在这个例子中,表达式 5+3
被求值为 8 ,然后结果被存放在匿名对象时。这个匿名对象的拷贝随后被传递给 printValue()
函数,然后被销毁。
注意,这使我们的代码变得非常简洁——我们不必用只使用一次的临时变量来弄乱代码。
匿名类对象
尽管我们前面的例子都是内置数据类型,但是我们也可以构造自定义类型的匿名对象。方法和创建普通对象类似,只需要省略变量名即可。
Cents cents { 5 }; // 普通变量
Cents { 7 }; // 匿名对象
在上面的例子中,Cents{ 7 }
会创建一个匿名对象并使用7作为其初始值,然后该对象就被销毁了。在这个例子中,匿名对象看上去没什么用。让我们看另一个例子吧:
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 #include <iostream>
class Cents
{
private :
int m_cents {};
public :
Cents ( int cents )
: m_cents { cents }
{}
int getCents () const { return m_cents ; }
};
void print ( const Cents & cents )
{
std :: cout << cents . getCents () << " cents \n " ;
}
int main ()
{
Cents cents { 6 };
print ( cents );
return 0 ;
}
注意,此示例与前面使用整数的示例非常相似。在本例中,我们的 main()
函数向print()
函数传递了一个Cents
类型的对象(名为cents)。
我们可以通过使用匿名对象来简化这个程序:
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 #include <iostream>
class Cents
{
private :
int m_cents {};
public :
Cents ( int cents )
: m_cents { cents }
{}
int getCents () const { return m_cents ; }
};
void print ( const Cents & cents )
{
std :: cout << cents . getCents () << " cents \n " ;
}
int main ()
{
print ( Cents { 6 }); // Note: Now we're passing an anonymous Cents value
return 0 ;
}
你可能希望程序打印如下内容:
现在,让我们再看一个更复杂的例子:
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 #include <iostream>
class Cents
{
private :
int m_cents {};
public :
Cents ( int cents )
: m_cents { cents }
{}
int getCents () const { return m_cents ; }
};
Cents add ( const Cents & c1 , const Cents & c2 )
{
Cents sum { c1 . getCents () + c2 . getCents () };
return sum ;
}
int main ()
{
Cents cents1 { 6 };
Cents cents2 { 8 };
Cents sum { add ( cents1 , cents2 ) };
std :: cout << "I have " << sum . getCents () << " cents. \n " ;
return 0 ;
}
在上面的例子中,我们使用了很多命名为Cents的值。在add()
函数中,我们有一个名为sum
的Cents
对象,我们使用它作为中间值,在返回·之前保存它。在函数main()
中,我们有另一个名为sum
的Cents
对象,也用作中间值。
我们可以通过使用匿名值来简化程序:
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 #include <iostream>
class Cents
{
private :
int m_cents {};
public :
Cents ( int cents )
: m_cents { cents }
{}
int getCents () const { return m_cents ; }
};
Cents add ( const Cents & c1 , const Cents & c2 )
{
// 列表初始化会参考函数返回值的类型
// 然后创建所需的对象
return { c1 . getCents () + c2 . getCents () }; // 返回匿名的 Cents value
}
int main ()
{
Cents cents1 { 6 };
Cents cents2 { 8 };
std :: cout << "I have " << add ( cents1 , cents2 ). getCents () << " cents. \n " ; // print anonymous Cents value
return 0 ;
}
这个版本的add()
函数与上面的函数相同,只是它使用了一个匿名的Cents值而不是一个命名变量。还要注意的是,在main()
中,我们不再使用命名的sum
变量作为临时存储。相反,我们会匿名使用 add()
的返回值!
因此,我们的程序更短、更清晰,而且通常更容易理解(一旦你理解了这个概念)。
事实上,因为cent1和cent2只在一个地方使用,我们可以进一步匿名化:
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 #include <iostream>
class Cents
{
private :
int m_cents {};
public :
Cents ( int cents )
: m_cents { cents }
{}
int getCents () const { return m_cents ; }
};
Cents add ( const Cents & c1 , const Cents & c2 )
{
return { c1 . getCents () + c2 . getCents () }; // return anonymous Cents value
}
int main ()
{
std :: cout << "I have " << add ( Cents { 6 }, Cents { 8 }). getCents () << " cents. \n " ; // print anonymous Cents value
return 0 ;
}
小结
在C++中,匿名对象主要用于传递参数或返回值而不必为此创建大量临时变量。动态分配的内存也是匿名完成的(这就是为什么它的地址必须分配给指针,否则我们就没有办法引用它)。
另外值得注意的是,因为匿名对象具有表达式作用域,所以它们只能使用一次(除非绑定到一个固定的左值引用 ,这将延长临时对象的生命周期,以匹配引用的生命周期)。如果需要在多个表达式中引用一个值,则应该使用命名变量。