C++中如何将对象传递给函数?
-
22-09-2019 - |
题
我是 C++ 编程新手,但我有 Java 编程经验。我需要有关如何在 C++ 中将对象传递给函数的指导。
我是否需要传递指针、引用或非指针和非引用值?我记得在 Java 中不存在这样的问题,因为我们只传递保存对象引用的变量。
如果您还可以解释在哪里使用这些选项,那就太好了。
解决方案
经验法则 C++11:
经过 按价值, ,除非当
- 你不需要对象的所有权,一个简单的别名就可以了,在这种情况下你 经过
const
参考, - 你必须改变对象,在这种情况下,使用 经过一个非
const
左值参考, - 您将派生类的对象作为基类传递,在这种情况下您需要 通过引用传递. 。(利用前面的规则来判断是否通过
const
参考与否。)
实际上从不建议通过指针传递。可选参数最好表示为 std::optional
(boost::optional
对于较旧的标准库),并且通过引用可以很好地完成别名。
C++11 的移动语义使得按值传递和返回更具吸引力,即使对于复杂对象也是如此。
经验法则 C++03:
传递参数 经过 const
参考, ,除非当
- 它们将在函数内部进行更改,并且此类更改应反映在外部,在这种情况下,您 经过非
const
参考 - 该函数应该可以在没有任何参数的情况下调用,在这种情况下,您可以通过指针传递,以便用户可以传递
NULL
/0
/nullptr
反而;应用之前的规则来确定是否应该 传递一个指向 a 的指针const
争论 - 它们是内置类型,可以是 通过副本
- 它们将在函数内部进行更改,并且此类更改应该 不是 被反射到外面,在这种情况下你可以 通过副本 (另一种方法是根据前面的规则传递并在函数内部进行复制)
(这里,“按值传递”被称为“按副本传递”,因为按值传递在C++03中总是创建一个副本)
还有更多内容,但是这几条初学者规则将使您走得更远。
其他提示
C++ 和 Java 中的调用约定存在一些差异。从技术上讲,C++ 中只有两个约定:值传递和引用传递,一些文献包括第三种指针传递约定(实际上是指针类型的值传递)。最重要的是,您可以向参数类型添加常量性,从而增强语义。
通过引用传递
通过引用传递意味着该函数从概念上讲将接收您的对象实例而不是它的副本。从概念上讲,引用是调用上下文中使用的对象的别名,并且不能为 null。函数内部执行的所有操作都适用于函数外部的对象。此约定在 Java 或 C 中不可用。
按值传递(和按指针传递)
编译器将在调用上下文中生成对象的副本,并在函数内使用该副本。函数内部执行的所有操作都是针对副本完成的,而不是针对外部元素。这是 Java 中基本类型的约定。
它的一个特殊版本是将指针(对象的地址)传递到函数中。该函数接收指针,并且应用于指针本身的任何和所有操作都将应用于副本(指针),另一方面,应用于取消引用的指针的操作将应用于该内存位置的对象实例,因此该函数可能会产生副作用。使用指向对象的指针按值传递的效果将允许内部函数修改外部值,就像按引用传递一样,并且还允许使用可选值(传递空指针)。
这是当函数需要修改外部变量时在 C 中使用的约定,以及在 Java 中使用引用类型的约定:引用被复制,但引用的对象是相同的:对引用/指针的更改在函数外部不可见,但对指向的内存的更改可见。
将 cons 添加到方程中
在 C++ 中,在不同级别定义变量、指针和引用时,可以为对象分配常量。您可以将变量声明为常量,可以声明对常量实例的引用,并且可以定义所有指向常量对象的指针、指向可变对象的常量指针和指向常量元素的常量指针。相反,在 Java 中,您只能定义一级常量(final 关键字):变量的引用(基元类型的实例,引用类型的引用),但不能定义对不可变元素的引用(除非类本身是不可变的)。
这在 C++ 调用约定中广泛使用。当对象很小时,您可以按值传递对象。编译器将生成一个副本,但该副本并不是一个昂贵的操作。对于任何其他类型,如果函数不会更改对象,则可以传递对该类型的常量实例(通常称为常量引用)的引用。这不会复制对象,而是将其传递到函数中。但同时编译器会保证函数内部对象不会被改变。
经验法则
这是一些需要遵循的基本规则:
- 对于原始类型更喜欢按值传递
- 对于其他类型,更喜欢通过引用传递常量
- 如果函数需要修改参数,请使用引用传递
- 如果参数是可选的,则使用指针传递(如果不应修改可选值,则使用常量)
这些规则还有其他一些小偏差,其中第一个是处理对象的所有权。当使用 new 动态分配对象时,必须使用 delete (或其 [] 版本)释放该对象。负责销毁对象的对象或函数被视为资源的所有者。当在一段代码中创建动态分配的对象,但所有权转移到不同的元素时,通常使用传递指针语义来完成,或者如果可能的话使用智能指针来完成。
边注
坚持 C++ 和 Java 引用之间差异的重要性非常重要。在 C++ 中,引用在概念上是对象的实例,而不是对象的访问器。最简单的例子是实现交换函数:
// 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 );
}
上面的交换函数 变化 其论点均通过引用的使用。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 指针,通过值传递到函数中。
有几种情况来考虑。
参数修改( “输入/输出” 参数 “out” 和)
void modifies(T ¶m);
// vs
void modifies(T *param);
此情况下,主要是关于风格:你想要的代码看起来像的调用(OBJ)的或通话(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);
这是有趣的例子。经验法则是“廉价复制”的类型是按值传递 - 这通常是小的类型(但不总是) - 而另一些则通过常量REF通过。但是,如果你需要让你的函数内的副本,无论你的应值通过。 (是的,这暴露了一个位实现的细节。来自C'est文件C ++。)
...和可选的
void uses(T const *param=0); // default value optional, too
// vs
void uses();
void uses(T const ¶m); // or optional(T param)
有最少的区别就在这里的所有情况之间,所以选择哪个让你的生活最容易的。
CONST由值是一个实现细节
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
通过值:
void func (vector v)
传递变量由值时该函数需要完全隔离来自环境即防止功能从修改原始变量以及防止其他线程同时被执行的功能被修改它的值。
缺点是CPU周期和额外的存储器花费复制的对象。
通行证通过const引用:
void func (const vector& v);
此形式模拟传递按值的行为,同时除去复制的开销。的函数获得对原始对象读访问,但不能修改它的值。
的缺点是线程安全:通过另一个线程的原始对象所做的任何变化将显示在函数内部,而它仍然执行
通行证由非const引用:
void func (vector& v)
使用此时该函数具有写回一些值的变量,这将最终习惯于由呼叫者。
就像const引用的情况下,这不是线程安全的。
通行证由常量指针:
void func (const vector* vp);
功能与由常量引用通除了不同的语法,加的事实,即调用函数可以传递NULL指针以指示它没有有效数据通过。
不是线程安全的。
通行证由非const指针:
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 ++有拷贝构造函数,其中当包含指针的对象传递给函数的参数,将停止操纵其他对象的数据,您将创建新的内存,爪哇的价值确实通和值为基准,所以它不需要拷贝构造函数。
将对象作为参数传递给函数有以下三种方法:
- 通过引用传递
- 按值传递
- 在参数中添加常量
通过以下示例:
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
以下是通过在C参数/参数到功能方式++
<强> 1。由值强>
// 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();
}