关于类:在C ++中,我无法掌握指针和类

In C++ I Cannot Grasp Pointers and Classes

我刚从大学毕业,现在已经在C++中工作了一段时间了。我理解C++的所有基本知识并使用它们,但我很难掌握更高级的主题,比如指针和类。我读过一些书和教程,我理解其中的例子,但是当我看到一些高级的现实生活例子时,我就无法理解它们。这让我很痛苦,因为我觉得它阻止了我把C++编程带到下一个层次。还有人有这个问题吗?如果是这样,你是怎么突破的?有人知道任何真正描述指针和类概念的书或教程吗?或者是使用高级指针和类技术的具有良好描述性注释的示例代码?任何帮助都将不胜感激。


理解C/C++中的指针

在理解指针如何工作之前,有必要了解变量在程序中是如何存储和访问的。每个变量都有两个部分:(1)存储数据的内存地址;(2)存储数据的值。

内存地址通常被称为变量的左值,存储的数据的值被称为右值(L和R表示左和右)。

考虑声明:

1
int x = 10;

在内部,程序将一个内存地址与变量x相关联。在这种情况下,我们假设程序将x分配给驻留在地址1001(不是一个实际地址,而是为了简单起见而选择的)。因此,x的左值(内存地址)是1001,x的右值(数据值)是10。

只需使用变量"x"即可访问右值。为了访问左值,需要"address of"运算符("&;")。表达式"x"被读取为"x的地址"。

1
2
3
4
Expression          Value
----------------------------------
x                   10
&x                  1001

存储在x中的值可以随时更改(例如x=20),但x(&x)的地址永远不能更改。

指针只是一个变量,可以用来修改另一个变量。它通过为其右值提供一个内存地址来实现这一点。也就是说,它指向内存中的另一个位置。

创建指向"x"的指针的操作如下:

1
int* xptr = &x;

"int*"告诉编译器我们正在创建一个指向整数值的指针。"=&x"部分告诉编译器我们正在将x的地址分配给xptr的右值。因此,我们告诉编译器xptr"指向"x。

假设xptr分配给内存地址1002,那么程序的内存可能如下所示:

1
2
3
4
Variable    lvalue    rvalue
--------------------------------------------
x           1001      10  
xptr        1002      1001

拼图的下一个部分是"间接运算符"("*"),其用法如下:

1
int y = *xptr;

间接运算符告诉程序将xptr的右值解释为内存地址,而不是数据值。也就是说,程序查找存储在xptr(1001)提供的地址的数据值(10)。

把它们放在一起:

1
2
3
4
5
6
7
Expression      Value
--------------------------------------------
x                   10
&x                  1001
xptr                1001
&xptr               1002
*xptr               10

现在已经解释了这些概念,下面是一些演示指针功能的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int x = 10;
int *xptr = &x;

printf("x = %d
"
, x);
printf("&x = %d
"
, &x);        
printf("xptr = %d
"
, xptr);
printf("*xptr = %d
"
, *xptr);

*xptr = 20;

printf("x = %d
"
, x);
printf("*xptr = %d
"
, *xptr);

对于输出,您将看到(注意:内存地址每次都不同):

1
2
3
4
5
6
x = 10
&x = 3537176
xptr = 3537176
*xptr = 10
x = 20
*xptr = 20

请注意,为"*xptr"赋值是如何更改"x"的值的。这是因为"*xptr"和"x"指的是内存中相同的位置,正如"&x"和"xptr"具有相同的值所证明的那样。


指针和类在C++中不是真正的高级主题。它们是非常基本的。

对我来说,当我开始用箭头画盒子的时候,指针就变硬了。为int绘制一个框。int*现在是一个单独的框,箭头指向int框。

所以:

1
2
int foo = 3;           // integer
int* bar = &foo;       // assigns the address of foo to my pointer bar

使用指针框(栏),我可以选择查看框中的地址。(这是foo的内存地址)。或者我可以操纵任何我有地址的东西。这个操作意味着我要沿着箭头指向整数(foo)。

1
2
*bar = 5;  // asterix means"dereference" (follow the arrow), foo is now 5
bar = 0;   // I just changed the address that bar points to

课程完全是另一个主题。有一些关于面向对象设计的书,但我不知道对于我头脑中的初学者来说是好的。你可能会有一本介绍Java的书。


这个链接有一个描述指针如何工作的视频,和粘土。信息丰富,易于消化。

这个页面有一些关于基本类的好信息。


我以前有一个问题,理解帕斯卡的指针回来:)一旦我开始做汇编指针实际上是唯一的方法访问内存,它只是击中我。这听起来像是一个远大的尝试,但尝试汇编程序(这总是一个好主意,尝试和理解计算机真正是关于什么)可能会教你指针。课程——好吧,我不明白你的问题——你的学校纯粹是结构化的程序设计吗?一个类只是观察现实生活模型的一种合乎逻辑的方式——你试图解决一个问题,这个问题可以归结为许多对象/类。


指针和类是完全不同的主题,所以我不会像这样把它们放在一起。在这两者中,我认为指针更为基本。

学习什么是指针的一个好练习是:

  • 创建链接列表
  • 从头到尾重复它
  • 倒转方向,使头部现在在后面,而背部现在在头部
  • 首先在白板上完成这一切。如果你能很容易地做到这一点,你就不需要再去理解什么是指针了。


    我们只是在讨论午餐时C++和OO的一些方面,实际上有人(一个伟大的工程师)说除非你在学习C++之前有一个非常强大的编程背景,否则它会毁了你。

    我强烈建议先学习另一种语言,然后在需要时切换到C++。指针并不是什么伟大的东西,它们只是编译器在没有指针的情况下将操作有效地转换为汇编时遗留下来的一部分。

    现在,如果编译器不能更好地优化数组操作,那么就不能使用指针,那么编译器就坏了。

    请不要误会我的意思,我并不是说C++是可怕的或者什么的,也不想开始一个倡导性的讨论,我已经使用它并且偶尔使用它,我只是推荐你从别的东西开始。

    这真的不像学习驾驶一辆手动汽车然后很容易地将其应用到自动汽车上,更像是学习驾驶那些巨大的建筑起重机,然后假设这将适用于当你开始驾驶一辆汽车-然后你发现你自己驾驶着你的车以5英里/小时的速度行驶在街道中央,你的应急灯亮着。

    [编辑]回顾最后一段--我认为这可能是我有史以来最准确的类比!


    其他答案中似乎已经提到了指针(没有双关语)。

    类是OO的基础。我费了很大的劲才把脑袋拧成十年都没有成功的样子。最终帮助我的书是克雷格·拉曼的《应用UML和模式》。我知道这听起来好像是关于一些不同的东西,但它确实很好地帮助你进入了类和对象的世界。


    指针:

    我发现这篇文章对指针的讨论非常周到。也许这会有帮助。你熟悉参考资料吗,比如C?这实际上是指别的东西?这可能是理解指针的良好开端。

    另外,看看肯特·弗雷德里克在下面的帖子,以另一种方式向你介绍自己的观点。


    练习没有替代品。

    读完一本书或听一个讲座很容易,感觉就像在跟踪正在发生的事情。

    我建议您采取一些代码示例(我假设您在磁盘上的某个地方),编译它们并运行它们,然后尝试将它们更改为执行不同的操作。

    • 向层次结构中添加另一个子类
    • 向现有类添加方法
    • 更改迭代的算法通过集合转发相反,是向后。

    我认为没有什么"银弹"书能做到这一点。

    对我来说,指针意味着什么在汇编中工作,看到指针实际上只是一个地址,而拥有指针并不意味着它所指向的是一个有意义的对象。


    学习汇编语言,然后学习C。然后你就会知道机器的基本原理是什么(以及更多的指针)。

    指针和类是C++的基本方面。如果你不理解它们,那就意味着你并不真正理解C++。

    就我个人而言,我对C++保持了几年的时间,直到我感觉到我对C语言有了很好的理解,而在汇编语言中,引擎盖下发生了什么。虽然这是很久以前的事了,但我认为了解计算机在低水平下的工作方式确实对我的职业生涯有益。

    学习编程可能需要很多年,但你应该坚持下去,因为这是一个非常有益的职业。


    在类的例子中,我有三种技术可以真正帮助我进入真正的面向对象编程。

    首先,我在一个游戏项目中大量使用类和对象,大量使用泛化(一种或是一种关系,例如学生是一种人)和构图(有一种关系,例如学生有一个学生贷款)。分解这段代码需要大量的工作,但真正将事情带到了透视图中。

    第二件有用的事情是在我的系统分析类中,我必须在那里制作http://www.agilemodeling.com/artifacts/classdiagram.htm">UML类图。我刚刚发现的这些帮助我理解程序中类的结构。

    最后,我在我的大学里教学生编程。关于这一点,我只能说你通过教书和观察别人解决问题的方法学到了很多东西。很多时候,一个学生会尝试一些我从来没有想过的事情,但通常很有意义,他们只是在实现自己的想法时遇到问题。

    我最好的建议是,这需要大量的练习,你的计划越多,你就越能理解它。


    为了理解这些要点,我不能高度推荐K&R书。


    真正帮助我理解这些概念的事情之一是学习UML——统一建模语言。以图形格式看到面向对象设计的概念确实帮助我了解了它们的含义。有时仅仅通过查看源代码实现它们来理解这些概念可能很难理解。

    以图形形式查看类似继承这样的面向对象的范例是掌握这个概念的一种非常强大的方法。

    马丁福勒的UML是一个很好的,简要介绍。


    你的问题似乎是C++中的C核心,而不是C++本身。为自己准备Kernighan&Ritchie(C编程语言)。吸气。它是非常好的东西,是有史以来最好的编程语言书籍之一。


    我在这些主题上读到的最好的书是Bruce Eckel的C++思想。你可以在这里免费下载。


    指针不是什么神奇的东西,你一直在使用它们!当你说:

    国际会计准则;

    编译器为"a"生成存储,实际上您是说声明了一个int,并希望将其内存位置命名为"a"。

    当你说:

    INT*A;

    您声明的变量可以保存int的内存位置。就这么简单。另外,不要害怕指针算法,只要当你处理指针和思考的时候,记住一个"记忆地图"浏览内存地址。

    C++中的类只是定义抽象数据类型的一种方式。我建议阅读一本好的OOP书来理解这个概念,然后,如果你感兴趣的话,学习C++编译器是如何生成代码来模拟OOP的。但是,如果你坚持C++足够长的时间,这些知识就会及时出现。


    你可能会发现乔尔的这篇文章很有启发性。顺便说一下,如果你在C++中工作了一段时间,并且已经在CS中毕业了,你可能已经去了JavaStudio(我认为你根本没有在C++中工作过,你一直在C中工作,但是使用C++编译器)。

    另外,第二个是Hoouu和N桑德尔的答案,指针对于C++来说是非常重要的。如果你不理解指针,那么你就不理解C++的基本知识(顺便说一下,认识到这一事实是理解C++的开始)。同样,如果你不理解类,那么你就不理解C++的基础知识(或者说是面向对象)。

    对于指针,我认为用框绘制是一个好主意,但是在装配中工作也是一个好主意。我认为,任何使用相对寻址的指令都会让你很快理解什么是指针。

    至于类(和面向对象的编程更一般),我建议STououStruts"C++编程语言"最新版本。它不仅是规范的C++参考资料,而且在许多其他方面也有相当多的素材,从基本的面向对象的类层次结构和继承一直到大型系统的设计原则。这是一本非常好的读物(如果不是有点厚,也很简洁)。


    对于指针和类,这里是我的类比。我要用一副牌。牌组有面值和类型(9个红心,4个黑桃等)。因此,在我们的"卡牌"类C++编程语言中,我们会说如下:

    1
    HeartCard card = 4; // 4 of hearts!

    现在,你知道四颗心在哪里,因为格利,你拿着甲板,面朝上,在上面!所以对于其余的牌,我们只说红心4号在开始。所以,如果我问你一开始是什么牌,你会说,"当然是四颗心!"好吧,你只是把我"指"到牌的位置。在我们的"卡片组"编程语言中,您也可以这样说:

    1
    2
    HeartCard card = 4; // 4 of hearts!
    print &card // the address is BEGINNING!

    现在,把你的牌翻过来。背面现在开始,你不知道这张卡是什么。但是,假设你想做什么就做什么,因为你充满了魔力。让我们在我们的"卡片组"里做这个!

    1
    2
    3
    HeartCard *pointerToCard = MakeMyCard("10 of hearts" );
    print pointerToCard // the value of this is BEGINNING!
    print *pointerToCard // this will be 10 of hearts!

    好吧,makemycard("心的10")是你在做你的魔法,知道你想指向开始,使卡片成为心的10!你把卡翻过来,瞧!现在,*可能会让你失望。如果是,请查看:

    1
    2
    3
    4
    5
    6
    HeartCard *pointerToCard = MakeMyCard("10 of hearts" );
    HeartCard card = 4; // 4 of hearts!
    print *pointerToCard; // prints 10 of hearts
    print pointerToCard; // prints BEGINNING
    print card; // prints 4 of hearts
    print &card; // prints END - the 4 of hearts used to be on top but we flipped over the deck!

    至于类,我们在示例中通过将类型定义为heartcard来使用类。我们知道什么是心牌…这是一张有价值和心型的卡片!所以,我们把它归类为心卡。每种语言都有类似的方式来定义或"分类"你想要的东西,但它们都有相同的概念!希望这有帮助…


    为了更好地理解指针,我认为,查看汇编语言如何处理指针可能很有用。指针的概念实际上是汇编语言和x86处理器指令体系结构的基本部分之一。也许这会让你觉得指针是程序的自然组成部分。

    至于类,除了OO范式之外,我认为从低级二进制的角度来看类可能很有趣。在基本层面上,它们在这方面并没有那么复杂。

    如果您想更好地理解C++对象模型下面的内容,可以在C++对象模型内部阅读。


    那本为我解题的书是唐纳德·阿尔科克(DonaldAlcock)的《美国国家标准学会C》(ANSIC)的插图。它充满了手绘样式框和箭头图,说明指针、指针算术、数组、字符串函数等。

    显然这是一本"C"书,但对于核心基础知识来说,这本书很难击败。


    在某种意义上,您可以将"指针"视为软件中最基本的两种类型之一,另一种是"值"(或"数据"),存在于一个巨大的可唯一寻址内存单元块中。想想看。对象和结构等并不存在于内存中,只有值和指针才存在。实际上,指针也是一个值……内存地址的值,而内存地址又包含另一个值……等等。

    因此,在C/C++中,当你声明一个"int"(INTA)时,你定义了一个32位的内存块,它包含一个值——一个数字。如果您随后声明一个"int pointer"(intb),您将定义一个包含int地址的32位内存块。我可以通过声明"intb=&;int a"将后者分配给前者,现在定义为intb的32位内存包含一个对应于inta在内存中的位置的地址。

    当您"取消引用"intb指针时,您将查看存储在intb内存中的地址,找到该位置,然后查看存储在那里的值(一个数字)。

    通常,当人们在使用"&;"、"*"和"->"运算符时,无法准确地了解所处理的内容时,我会感到困惑——它是地址、值还是什么?您只需要关注这样一个事实:内存地址只是位置,值是存储在那里的二进制信息。


    从拉塞维克对类似问题的回答来看:

    Pointers is a concept that for many
    can be confusing at first, in
    particular when it comes to copying
    pointer values around and still
    referencing the same memory block.

    I've found that the best analogy is to
    consider the pointer as a piece of
    paper with a house address on it, and
    the memory block it references as the
    actual house. All sorts of operations
    can thus be easily explained:

    • Copy pointer value, just write the address on a new piece of paper
    • Linked lists, piece of paper at the house with the address of the next
      house on it
    • Freeing the memory, demolish the house and erase the address
    • Memory leak, you lose the piece of paper and cannot find the house
    • Freeing the memory but keeping a (now invalid) reference, demolish the
      house, erase one of the pieces of
      paper but have another piece of paper
      with the old address on it, when you
      go to the address, you won't find a
      house, but you might find something
      that resembles the ruins of one
    • Buffer overrun, you move more stuff into the house than you can
      possibly fit, spilling into the
      neighbours house

    假设指针是数组地址。

    1
    2
    3
    x = 500; // memory address for hello;
    MEMORY[x] ="hello";
    print  MEMORY[x];

    这是一个过于简单化的图形,但在大多数情况下,只要你不想知道这个数字是什么或手工设置它,你就可以了。

    当我了解C时,我有一些宏,或多或少允许您使用指针,就像它们是内存中的数组索引一样。但我早就失去了代码,早就忘记了。

    我记得是从

    1
    2
    #define MEMORY 0;
    #define MEMORYADDRESS( a ) *a;

    这一点本身就很难发挥作用。希望其他人能扩展这个逻辑。


    你读过Bjarne Stroustrup的C++程序设计语言吗?他创建了C++。

    C++ FAQ Lite也不错。


    课程相对容易掌握;OOP需要你很多年。就我个人而言,我直到去年才完全掌握了真正的OOP。糟糕的是,在大学里,闲聊并没有它应有的那么普遍。它真正让我们明白OOP是关于对象交易消息的,而不是类是带有函数的自包含全局变量。

    如果你真的是新手,那么这个概念需要一段时间才能理解。当我在10年级第一次遇到他们时,直到有人知道他们在做什么,我才明白他们在做什么,然后解释发生了什么。我建议你试试看。


    我真正得到指针的地方是在一个fatmac(大约1984年左右)上编写turbopascal代码,当时它是本地的mac语言。

    MAC有一个奇怪的内存模型,在分配地址时,内存存储在堆上的指针中,但不保证该地址本身的位置,而是内存处理例程返回指向指针的指针(称为句柄)。因此,要访问分配的内存的任何部分,必须取消对句柄的引用两次。这花了一段时间,但不断的练习最终把功课带回家了。

    Pascal的指针处理比C++更容易掌握,其中语法对初学者没有帮助。如果你真的很难理解C语言中的指针,那么你最好的选择可能是获得一个Pascal编译器的副本,并尝试在其中编写一些基本的指针代码(Pascal接近C语言,你将在几个小时内获得基本的指针代码)。链接列表等是个不错的选择。一旦你对C++的恢复感到满意,掌握了概念,你会发现悬崖看起来不会那么陡峭。


    上课:

    对我来说,突破性的时刻是当我了解到接口的时候。抽象出你如何写作的细节的想法解决了一个问题,并且只给出一个与类交互的方法列表,这是非常有见地的。

    事实上,我的教授明确地告诉我们,他将把我们的课程插入他的测试工具中来给我们的课程评分。评分将根据他给我们的要求和程序是否崩溃来进行。

    长话短说,类可以让您总结功能并以更简洁的方式调用它(大多数情况下,总是有异常)