扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
c++ 与java引用具体比较:
让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:申请域名、网页空间、营销软件、网站建设、福山网站维护、网站推广。
c++中一个引用指向的地址不会改变,改变的是指向地址的内容,然而java中引用指向的地址在变!!
如果非要对比着看,那么Java中的“引用”倒是和C/C++的指针更像一些,和C++的“引用”很不一样。
java去除指针概念,就用引用罗...
你看 java:
A a = new A(1);
A b = new A(2);
b = a;
没有问题,a 和 b引用同一个对象A(2),原来的A(1)成为没有被引用的对象。 垃圾回收机制会在之后的某个时刻把A(1)干掉。
而C++则不然。C++的引用就语义上说是“别名”【本质是个const指针,又叫指针常量】,而并不是指针的另一种用法:
A a = A(1);
A b = A(2);
A c = b; //c 是 b的别名
c = a; //并不是 c 引用 a,而是拷贝操作 c.operator= ( a )
就语言机制来说,java的引用是用来管理和命名对象;
而,C++的引用机制是很纯粹的,就是别名而已。
每种语言的特性都是整体的有机部分。
我们知道, java的引用机制是一个很复杂的机制。他必须区分“基本对象”和“复合对象”,你可以想象一下,如果其中没有基本对象,那么我们如何完成对象的复制? 唯一的解决方案是提供两个等于号,或者一律用构造函数.... 但是综合来看,他和垃圾回收形成了相当完美的组合方案。
而C++ 的引用机制为运算符重载提供了大幅度的支持。C++ 的引用是用类“模拟”基本对象的根本要求。 如果C++使用java那种引用,那么原本漂亮的 operator[]、 proxy class 等就很难实现了。 更进一步, C++ 的运算符重载对 C++ 的模版机制提供了强力的支持
在c++中,引用只是对于一个变量起的别名,一旦定义就无法修改,即无法再指向其他变量,如程序中,对于a的引用的任何操作都等同于对于a的操作。
java定义的引用并不是这样。在java中,引用相当与指针,它与c中的指针主要有两个区别:一是引用不能进行地址操作,如数组的加一 操作,相当于引用只是只是指向数据的一个副本,而不是数据本身,这样就避免了由于对于地址的误操作而改变其他变量的值,甚至危害到系统的安全。二是 java中的引用只能指向对象,他的引用是在实例化对象时系统直接生成的,因此对于普通数据类型是不能进行引用定义的,如果要对普通数据类型进行函数调用 时的地址传递(即java中的引用传递),必须把数据封装到类中。
java的这种特性使得在java的函数或类的参数传递时可以实现与c中指针相同的功能。
具体应用:
指针和引用在C++中很常用,但是对于它们之间的区别很多初学者都不是太熟悉,下面来谈谈他们2者之间的区别和用法。
1.指针和引用的定义和性质区别:
(1)指针:指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;而引用跟原来的变量实质上是同一个东西,只不过是原变量的一个别名而已。如:
int a=1;int *p=a;
int a=1;int b=a;
上面定义了一个整形变量和一个指针变量p,该指针变量指向a的存储单元,即p的值是a存储单元的地址。
而下面2句定义了一个整形变量a和这个整形a的引用b,事实上a和b是同一个东西,在内存占有同一个存储单元。
(2)可以有const指针,但是没有const引用;
(3)指针可以有多级,但是引用只能是一级(int **p;合法 而 int a是不合法的)
(4)指针的值可以为空,但是引用的值不能为NULL,并且引用在定义的时候必须初始化;
(5)指针的值在初始化后可以改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了。
(6)"sizeof引用"得到的是所指向的变量(对象)的大小,而"sizeof指针"得到的是指针本身的大小;
(7)指针和引用的自增(++)运算意义不一样;
2.指针和引用作为函数参数进行传递时的区别。
(1)指针作为参数进行传递:
#includeiostreamusing namespace std;void swap(int *a,int *b)
{
int temp=*a;
*a=*b;
*b=temp;
}int main(void)
{
int a=1,b=2;
swap(a,b);
couta" "bendl;
system("pause");
return 0;
}
结果为2 1;
用指针传递参数,可以实现对实参进行改变的目的,是因为传递过来的是实参的地址,因此使用*a实际上是取存储实参的内存单元里的数据,即是对实参进行改变,因此可以达到目的。
再看一个程序;
#includeiostreamusing namespace std;void test(int *p)
{
int a=1;
p=a;
coutp" "*pendl;
}int main(void)
{ int *p=NULL;
test(p); if(p==NULL)
cout"指针p为NULL"endl;
system("pause"); return 0;
}
运行结果为:
0x22ff44 1
指针p为NULL
大家可能会感到奇怪,怎么回事,不是传递的是地址么,怎么p回事NULL?事实上,在main函数中声明了一个指针p,并赋值为NULL,当调用test函数时,事实上传递的也是地址,只不过传递的是指地址。也就是说将指针作为参数进行传递时,事实上也是值传递,只不过传递的是地址。当把指针作为参数进行传递时,也是将实参的一个拷贝传递给形参,即上面程序main函数中的p何test函数中使用的p不是同一个变量,存储2个变量p的单元也不相同(只是2个p指向同一个存储单元),那么在test函数中对p进行修改,并不会影响到main函数中的p的值。
如果要想达到也同时修改的目的的话,就得使用引用了。
2.将引用作为函数的参数进行传递。
在讲引用作为函数参数进行传递时,实质上传递的是实参本身,即传递进来的不是实参的一个拷贝,因此对形参的修改其实是对实参的修改,所以在用引用进行参数传递时,不仅节约时间,而且可以节约空间。
看下面这个程序:
#includeiostreamusing namespace std;void test(int a)
{
couta" "aendl;
}int main(void)
{ int a=1;
couta" "aendl;
test(a);
system("pause"); return 0;
}
输出结果为: 0x22ff44 1
0x22ff44 1
再看下这个程序:
这足以说明用引用进行参数传递时,事实上传递的是实参本身,而不是拷贝。
所以在上述要达到同时修改指针的目的的话,就得使用引用了。
#includeiostreamusing namespace std;void test(int *p)
{
int a=1;
p=a;
coutp" "*pendl;
}int main(void)
{ int *p=NULL;
test(p); if(p!=NULL)
cout"指针p不为NULL"endl;
system("pause"); return 0;
}
输出结果为:0x22ff44 1
指针p不为NULL
C++ 引用的本质?深入分析C++引用:
引言
我选择写 C++ 中的引用是因为我感觉大多数人误解了引用。而我之所以有这个感受是因为我主持过很多 C++ 的面试,并且我很少从面试者中得到关于 C++ 引用的正确答案。
那么 c++ 中引用到底意味这什么呢?通常一个引用让人想到是一个引用的变量的别名,而我讨厌将 c++ 中引用定义为变量的别名。这篇文章中,我将尽量解释清楚, c++ 中根本就没有什么叫做别名的东东。
背景
在 c/c++ 中,访问一个变量只能通过两种方式被访问,传递,或者查询。这两种方式是:
1. 通过值 访问 / 传递变量
2. 通过地址 访问 / 传递变量 – 这种方法就是指针
除此之外没有第三种访问和传递变量值的方法。引用变量也就是个指针变量,它也拥有内存空间。最关键的是引用是一种会被编译器自动解引用的指针。很难相信么?让我们来看看吧。。。
下面是一段使用引用的简单 c++ 代码
[cpp] view plain copy print?
#include iostream.h
int main()
{
int i = 10; // A simple integer variable
int j = i; // A Reference to the variable i
j++; // Incrementing j will increment both i and j.
// check by printing values of i and j
cout i j endl; // should print 11 11
// Now try to print the address of both variables i and j
cout i j endl;
// surprisingly both print the same address and make us feel that they are
// alias to the same memory location.
// In example below we will see what is the reality
return 0;
}
引用其实就是 c++ 中的指针常量。表达式 int i = j; 将会被编译器转化成 int *const i = j; 而引用之所以要初始化是因为 const 类型变量必须初始化,这个指针也必须有所指。下面我们再次聚焦到上面这段代码,并使用编译器的那套语法将引用替换掉。
[cpp] view plain copy print?
#include iostream.h
int main()
{
int i = 10; // A simple integer variable
int *const j = i; // A Reference to the variable i
(*j)++; // Incrementing j. Since reference variables are
// automatically dereferenced by compiler
// check by printing values of i and j
cout i *j endl; // should print 11 11
// A * is appended before j because it used to be reference variable
// and it should get automatically dereferenced.
return 0;
}
读者一定很奇怪为什么我上面这段代码会跳过打印地址这步。这里需要一些解释。因为引用变量时(使用变量时)会被编译器自动解引用的,那么一个诸如 cout j endl; 的语句,编译器就会将其转化成语句 cout *j endl; 现在 * 会相互抵消,这句话变的毫无意义,而 cout 打印的 j 值就是 i 的地址,因为其定义语句为 int *const j = i;
所以语句 cout i j endl; 变成了 cout i *j endl; 这两种情况都是打印输出 i 的地址。这就是当我们打印普通变量和引用变量的时候会输出相同地址的原因。
下面给出一段复杂一些的代码,来看看引用在级联 (cascading) 中是如何运作的。
[cpp] view plain copy print?
#include iostream.h
int main()
{
int i = 10; // A Simple Integer variable
int j = i; // A Reference to the variable
// Now we can also create a reference to reference variable.
int k = j; // A reference to a reference variable
// Similarly we can also create another reference to the reference variable k
int l = k; // A reference to a reference to a reference variable.
// Now if we increment any one of them the effect will be visible on all the
// variables.
// First print original values
// The print should be 10,10,10,10
cout i "," j "," k "," l endl;
// increment variable j
j++;
// The print should be 11,11,11,11
cout i "," j "," k "," l endl;
// increment variable k
k++;
// The print should be 12,12,12,12
cout i "," j "," k "," l endl;
// increment variable l
l++;
// The print should be 13,13,13,13
cout i "," j "," k "," l endl;
return 0;
}
下面这段代码是将上面代码中的引用替换之后代码,也就是说明我们不依赖编译器的自动替换功能,手动进行替换也能达到相同的目标。
[cpp] view plain copy print?
#include iostream.h
int main()
{
int i = 10; // A Simple Integer variable
int *const j = i; // A Reference to the variable
// The variable j will hold the address of i
// Now we can also create a reference to reference variable.
int *const k = *j; // A reference to a reference variable
// The variable k will also hold the address of i because j
// is a reference variable and
// it gets auto dereferenced. After and * cancels each other
// k will hold the value of
// j which it nothing but address of i
// Similarly we can also create another reference to the reference variable k
int *const l = *k; // A reference to a reference to a reference variable.
// The variable l will also hold address of i because k holds address of i after
// and * cancels each other.
// so we have seen that all the reference variable will actually holds the same
// variable address.
// Now if we increment any one of them the effect will be visible on all the
// variables.
// First print original values. The reference variables will have * prefixed because
// these variables gets automatically dereferenced.
// The print should be 10,10,10,10
cout i "," *j "," *k "," *l endl;
// increment variable j
(*j)++;
// The print should be 11,11,11,11
cout i "," *j "," *k "," *l endl;
// increment variable k
(*k)++;
// The print should be 12,12,12,12
cout i "," *j "," *k "," *l endl;
// increment variable l
(*l)++;
// The print should be 13,13,13,13
cout i "," *j "," *k "," *l endl;
return 0;
}
我们通过下面代码可以证明 c++ 的引用不是神马别名,它也会占用内存空间的。
[cpp] view plain copy print?
#include iostream.h
class Test
{
int i; // int *const i;
int j; // int *const j;
int k; // int *const k;
};
int main()
{
// This will print 12 i.e. size of 3 pointers
cout "size of class Test = " sizeof(class Test) endl;
return 0;
}
结论
我希望这篇文章能把 c++ 引用的所有东东都解释清楚,然而我要指出的是 c++ 标准并没有解释编译器如何实现引用的行为。所以实现取决于编译器,而大多数情况下就是将其实现为一个 const 指针。
引用支持 c++ 虚函数机制的代码
[cpp] view plain copy print?
#include iostream.h
class A
{
public:
virtual void print() { cout"A.."endl; }
};
class B : public A
{
public:
virtual void print() { cout"B.."endl; }
};
class C : public B
{
public:
virtual void print() { cout"C.."endl; }
};
int main()
{
C c1;
A a1 = c1;
a1.print(); // prints C
A a2 = c1;
a2.print(); // prints A
return 0;
}
上述代码使用引用支持虚函数机制。如果引用仅仅是一个别名,那如何实现虚函数机制,而虚函数机制所需要的动态信息只能通过指针才能实现,所以更加说明引用其实就是一个 const 指针。
补充:const 指针(指针常量)与指向const的指针(常量指针)
当使用带有const的指针时其实有两种意思。一种指的是你不能修改指针本身的内容,另一种指的是你不能修改指针指向的内容。听起来有点混淆一会放个例子上来就明白了。
先说指向const的指针,它的意思是指针指向的内容是不能被修改的。它有两种写法。
const int* p; (推荐)
int const* p;
第一种可以理解为,p是一个指针,它指向的内容是const int 类型。p本身不用初始化它可以指向任何标示符,但它指向的内容是不能被改变的。
第二种很容易被理解成是p是一个指向int的const指针(指针本身不能被修改),但这样理解是错误的,它也是表示的是指向const的指针(指针指向的内容是不能被修改的),它跟第一种表达的是一个意思。为了避免混淆推荐大家用第一种。
再说const指针,它的意思是指针本身的值是不能被修改的。它只有一种写法
int* const p=一个地址; (因为指针本身的值是不能被修改的所以它必须被初始化)
这种形式可以被理解为,p是一个指针,这个指针是指向int 的const指针。它指向的值是可以被改变的如*p=3;
还有一种情况是这个指针本身和它指向的内容都是不能被改变的,请往下看。
const int* const p=一个地址;
int const* const p=一个地址;
看了上面的内容是不是有点晕,没关系,你不用去背它,用的多了就知道了,还有个技巧,通过上面的观察我们不难总结出一点规律,是什么呢?也许你已经看出来了,什么!竟然没看也来,那只好还得听我唠叨了。这个规律就是: 指向const的指针(指针指向的内容不能被修改)const关健字总是出现在*的左边而const指针(指针本身不能被修改)const关健字总是出现在*的右边,那不用说两个const中间加个*肯定是指针本身和它指向的内容都是不能被改变的。有了这个规则是不是就好记多了。
什么还是晕,那就看下面的程序,你把它编译一下看看错误提示就明白了。
1 #include iostream
2
3 using namespace std;
4
5 int main(int argc, char *argv[])
6 {
7 int a=3;
8 int b;
9
10 /*定义指向const的指针(指针指向的内容不能被修改)*/
11 const int* p1;
12 int const* p2;
13
14 /*定义const指针(由于指针本身的值不能改变所以必须得初始化)*/
15 int* const p3=a;
16
17 /*指针本身和它指向的内容都是不能被改变的所以也得初始化*/
18 const int* const p4=a;
19 int const* const p5=b;
20
21 p1=p2=a; //正确
22 *p1=*p2=8; //不正确(指针指向的内容不能被修改)
23
24 *p3=5; //正确
25 p3=p1; //不正确(指针本身的值不能改变)
26
27 p4=p5;//不正确 (指针本身和它指向的内容都是不能被改变)
28 *p4=*p5=4; //不正确(指针本身和它指向的内容都是不能被改变)
29
30 return 0;
31 }
C语言是经典的面向过程的编程语言,编程入门一般都学C语言以了解编程以及锻炼逻辑思维能力,在一些跟硬件比较紧密的编程中也经常用到。\x0d\x0a\x0d\x0aC++是在C语言的基础上加入了面向对象的概念,成为混合型面向对象语言,功能强大,但难度也大。\x0d\x0a\x0d\x0aJava是在C++的基础上进行改进的,摒弃了一些C++的不足之处,语法跟C++很像,它是运行在Java虚拟机上,所以可以跨平台,一处编译,到处执行。\x0d\x0a\x0d\x0a编程都是易学难精,哪个都好学,哪个又都不好学。都有很好的发展前景,就看是什么方向了。底层的C、桌面应用程序、游戏等等C++、嵌入式,网页,Java。
String string1 = "i love you";
String string2 = "i love you";
在栈中分配两个空间,
其名为string1和string2
其值为两个相同的同时指向"i love you"的地址,"i love you"存放在data segment中只有一个。
String string3 = new String("i love you");
String string4 = new String("i love you");
在栈中分配两个空间,
其名为string3和string4
string3指向堆中为new String("i love you");(对象一)
新分配的空间,其值为指向这里的地址
string4指向堆中为new String("i love you")(对象二)
分配的空间;这是另一片空间,其值为指向这里的地址。
Java和C#都是编程的语言,它们是两个不同方向的两种语言
相同点:
他们都是面向对象的语言,也就是说,它们都能实现面向对象的思想(封装,继承,多态)
区别:
1.c#中的命名空间是namespace类似于Java中的package(包),在Java中导入包用import而c#中用using。
2.c#和Java都是从main函数入口的,但是c#中的main函数的首字母必须大写,它有四种写法如下:
static void Main(string args[]){}
static int Main(string args[]){}
static void Main(){}
static void Main(){}
而Java中只有一种形式:static void main(String [] args){}
3.数据类型:Java跟c#基本都差不多,但是Java的String类型的首字母必须大写,而c#中可以小写也可以大写,还有布尔型,Java中是boolean,c#中是bool。
4.变量的命名:Java中可以用$符号,而c#中不可以使用。
5.注释:Java比c#少一种"///"的文档注释。
6.输出:c#有三种方式输出:Cosole.WriteLine(); Cosole.WriteLine(要输出的值); Cosole.WriteLine("格式字符串",变量列表); 前两种的用法与Java中的ystem.out.println()方法的用法相同,第三种方式是根据占位符输出的,比Java更方便了。
7.控制流语句:c#跟Java类似,还有c#中的switch如果case后面有内容必须要有break;Java可以没有break;
8.数组:两种语言的声明都是用new关键字的。都可以在创建数组的同时初始化如:int a[]={1,2,3,5,5};但是c#比Java多两种初始化如:int a[]=new int[3]{1,2,3}; int a[]=new int[]{1,2,3};
9.方法中传递的参数:两种语言都使用值传递与引用传递。
C#的引用传递的关键字是ref与out,ref侧重于修改,out侧重于输出。而Java中都以传值方式;
10.访问修饰符:C#中的访问修饰符与Java中的基本对应,但多出了一个internal。简而言之,C#有5种类型的可访问性,如下所示:
public:成员可以从任何代码访问。 protected:成员只能从派生类访问。
internal:成员只能从同一程序集的内部访问。
protected:成员只能从同一程序集内的派生类访问。
private:成员只能在当前类的内部访问。
11.由于C#中不存在final关键词,如果想要某个类不再被派生,你可以使用sealed关键词密封。
12.集合:两种语言都有集合ArrayList,还有通过键访问值的Java中是HashMap而c#中是HashTable。c#比Java多泛型集合ListT与DictionaryK,V更容易了,无需拆箱装箱了,更安全了。
13.继承:Java中用关键字extends,c#只用":"就行了.调用父类的构造方法Java用super关键字,而c#用base关键字。
14.多态:抽象类和抽象方法两种语言都用abstract关键字。Java中另外一个类如果继承了它,实现直接重写此方法就可以了;而c#必须加上关键字override实现。C#还比Java多一种虚方法来实现多态。
15.接口:都用关键字interface定义,Java实现用关键字implements;c#用":"实现。在C#中,接口内的所有方法默认都是公用方法。在Java中,方法声明可以带有public修饰符(即使这并非必要),但在C#中,显式为接口的方法指定public修饰符是非法的。
16.C#中的is操作符与Java中的instanceof操作符一样,两者都可以用来测试某个对象的实例是否属于特定的类型。在Java中没有与C#中的as操作符等价的操作符。as操作符与is操作符非常相似,但它更富有"进取心":如果类型正确的话,as操作符会尝试把被测试的对象引用转换成目标类型;否则,它把变量引用设置成null。
17.枚举器即enum类型(java无),把它作为一个变量值的类型使用,从而把变量可能的取值范围限制为枚举器中出现的值。
18.结构(Struct)与类很相似,而结构是一种值类型,它存储在栈中或者是嵌入式的,结构可以实现接口,可以象类一样拥有成员,但结构不支持继承。
19.c#保留了指针。Unsafe。(C#使用指针不安全,最后这条有待考证)
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流