我需要传递指针、引用还是非指针和非引用的值?我记得在Java中没有这样的问题,因为我们只传递保存对象引用的变量。
如果您能解释在哪些情况下使用这些选项,那就太好了。
传递参数时,除非
几乎从不建议通过指针进行传递。可选参数最好表示为std::optional
(对于旧的std库,使用boost::optional
),而别名可以通过引用很好地实现。
C ++ 11的移动语义使得通过值进行传递和返回即使对于复杂对象也更具吸引力。
传递参数时,请按const引用方式传递,除非
NULL
/0
/nullptr
;按照前一条规则确定是否应该通过指向const参数的指针进行传递(这里,“按值传递”称为“按拷贝传递”,因为在C++03中按值传递始终会创建一个副本)
还有更多需要注意的地方,但这些初学者的规则已经足够使用了。
// C++
class Type; // defined somewhere before, with the appropriate operations
void swap( Type & a, Type & b ) {
Type tmp = a;
a = b;
b = tmp;
}
int main() {
Type a, b;
Type old_a = a, old_b = b;
swap( a, b );
assert( a == old_b );
assert( b == old_a );
}
上述的swap函数通过引用修改其两个参数。在Java中最接近的代码:
public class C {
// ...
public static void swap( C a, C b ) {
C tmp = a;
a = b;
b = tmp;
}
public static void main( String args[] ) {
C a = new C();
C b = new C();
C old_a = a;
C old_b = b;
swap( a, b );
// a and b remain unchanged a==old_a, and b==old_b
}
}
Java代码版本将在内部修改引用的副本,但不会在外部修改实际对象。 Java引用是没有指针算术运算的C指针,以按值传递到函数中。
需要考虑几种情况。
void modifies(T ¶m);
// vs
void modifies(T *param);
这个案例主要关于代码风格:你想让代码看起来像call(obj)还是call(&obj)?然而,在下面的可选情况和当你想要重载运算符时,有两个点需要注意不同之处。
void modifies(T *param=0); // default value optional, too
// vs
void modifies();
void modifies(T ¶m);
void uses(T const ¶m);
// vs
void uses(T param);
void uses(T const *param=0); // default value optional, too
// vs
void uses();
void uses(T const ¶m); // or optional(T param)
这里各种情况之间的差异最小,所以选择让你生活最轻松的方式。
void f(T);
void f(T const);
这些声明实际上是完全相同的函数!当按值传递时,const只是一种实现细节。试一试:
void f(int);
void f(int const) { /* implements above function, not an overload */ }
typedef void NC(int); // typedefing function types
typedef void C(int const);
NC *nc = &f; // nc is a function pointer
C *c = nc; // C and NC are identical types
const
是一种实现方法。 - balkivoid func (vector v)
当函数需要与环境完全隔离时,即防止函数修改原始变量以及在执行函数时防止其他线程修改其值时,请按值传递变量。
缺点是CPU周期和额外内存用于复制对象。
void func (const vector& v);
这个形式模拟了按值传递的行为,同时去除了复制开销。函数可以读取原始对象,但不能修改其值。
缺点是线程安全性:另一个线程对原始对象所做的任何更改将在函数执行时显示出来。
void func (vector& v)
当函数需要将某些值写回变量并最终由调用者使用时,请使用此选项。
与常量引用情况一样,这也不是线程安全的。
void func (const vector* vp);
与按const引用传递在功能上相同,除了语法不同外,调用函数可以传递NULL指针以指示它没有有效数据可传递。
不支持多线程安全。
void func (vector* vp);
类似于非const引用。调用方通常会在函数不会回写值时将变量设置为NULL。这种约定在许多glibc API中都可以看到。例如:
void func (string* str, /* ... */) {
if (str != NULL) {
*str = some_value; // assign to *str only if it's non-null
}
}
就像所有的按引用/指针传递一样,不是线程安全的。
由于没有人提到,我来补充一下。当你在C++中将一个对象传递给函数时,如果你没有自己的复制构造函数,那么默认的复制构造函数会被调用,它会创建一个对象的克隆并将其传递给方法。因此,当你改变对象的值时,这将反映在对象的副本上而不是原始对象上,这就是C++中的问题。因此,如果你将所有类属性都设置为指针,那么复制构造函数将复制指针属性的地址,因此当对对象进行方法调用以操作存储在指针属性地址中的值时,更改也会反映在作为参数传递的原始对象中,这可以像Java一样工作,但不要忘记所有类属性必须是指针,同时你应该改变指针的值,这将在代码解释中更清晰。
Class CPlusPlusJavaFunctionality {
public:
CPlusPlusJavaFunctionality(){
attribute = new int;
*attribute = value;
}
void setValue(int value){
*attribute = value;
}
void getValue(){
return *attribute;
}
~ CPlusPlusJavaFuncitonality(){
delete(attribute);
}
private:
int *attribute;
}
void changeObjectAttribute(CPlusPlusJavaFunctionality obj, int value){
int* prt = obj.attribute;
*ptr = value;
}
int main(){
CPlusPlusJavaFunctionality obj;
obj.setValue(10);
cout<< obj.getValue(); //output: 10
changeObjectAttribute(obj, 15);
cout<< obj.getValue(); //output: 15
}
但这不是一个好主意,因为你最终会写很多涉及指针的代码,这些指针容易出现内存泄漏问题,而且不要忘记调用析构函数。为了避免这种情况,C++提供了复制构造函数,当包含指针的对象被传递给函数参数时,将创建新的内存,以防止操作其他对象的数据。Java采用按值传递方式,值是引用,因此不需要复制构造函数。
在编写函数和选择参数类型时,我需要传递指针、引用还是非指针和非引用值?
这是一个很重要的问题,它会影响到函数的调用方式,具体取决于一些因素。
最简单的选择是按值传递对象。这基本上在函数中创建了对象的副本,这有许多优点。但有时复制是昂贵的,在这种情况下,通常最好使用常量引用const&
。有时您需要通过函数更改对象。然后需要使用非常量引用&
。
有关参数类型选择的指导,请参见C++核心准则的函数部分,从F.15开始。作为一般规则,请尽量避免使用裸指针*
。
将对象作为参数传递给函数有三种方法:
请查看以下示例:
class Sample
{
public:
int *ptr;
int mVar;
Sample(int i)
{
mVar = 4;
ptr = new int(i);
}
~Sample()
{
delete ptr;
}
void PrintVal()
{
cout << "The value of the pointer is " << *ptr << endl
<< "The value of the variable is " << mVar;
}
};
void SomeFunc(Sample x)
{
cout << "Say i am in someFunc " << endl;
}
int main()
{
Sample s1= 10;
SomeFunc(s1);
s1.PrintVal();
char ch;
cin >> ch;
}
输出:
假设我在someFunc函数中
指针的值为-17891602
变量的值为4
// passing parameters by value . . .
void foo(int x)
{
x = 6;
}
2. 通过引用。
// passing parameters by reference . . .
void foo(const int &x) // x is a const reference
{
x = 6;
}
// passing parameters by const reference . . .
void foo(const int &x) // x is a const reference
{
x = 6; // compile error: a const reference cannot have its value changed!
}
3. 通过对象。
class abc
{
display()
{
cout<<"Class abc";
}
}
// pass object by value
void show(abc S)
{
cout<<S.display();
}
// pass object by reference
void show(abc& S)
{
cout<<S.display();
}