В С++ я не могу понять указатели и классы

Я недавно вышел из колледжа и уже некоторое время работаю на С++. Я понимаю все основы С++ и использую их, но мне трудно усвоить более сложные темы, такие как указатели и классы. Я прочитал несколько книг и руководств, и я понимаю примеры в них, но потом, когда я смотрю на некоторые примеры реальной жизни, я не могу понять их. Это убивает меня, потому что я чувствую, что это мешает мне привести мое программирование на С++ на следующий уровень. У кого-нибудь еще была эта проблема? Если да, то как вы это прорвали? Кто-нибудь знает о каких-либо книгах или учебниках, которые действительно описывают указатели и концепции классов? или, может быть, некоторый пример кода с хорошими описательными комментариями с использованием передовых указателей и методов класса? любая помощь будет принята с благодарностью.

Ответ 1

Указатели и классы на самом деле не являются продвинутыми темами на С++. Они довольно фундаментальные.

Для меня указатели затвердели, когда я начал рисовать прямоугольники со стрелками. Нарисуйте поле для int. И int * теперь представляет собой отдельный блок со стрелкой, указывающей на поле int.

Итак:

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

С моим полем указателя (bar) у меня есть выбор либо взглянуть на адрес внутри поля. (Каков адрес памяти foo). Или я могу манипулировать тем, на что у меня есть адрес. Это манипуляция означает, что я следую этой стрелке в целое число (foo).

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

Классы - это еще одна тема. Там есть несколько книг по объектно-ориентированному дизайну, но я не знаю хороших для новичков на моей голове. Возможно, вам повезло со встроенной Java-книгой.

Ответ 2

Понимание указателей в C/С++

Прежде чем понять, как работают указатели, необходимо понять, как переменные хранятся и доступны в программах. Каждая переменная имеет две части - (1) адрес памяти, в которой хранятся данные, и (2) значение сохраненных данных.

Адрес памяти часто называют lvalue переменной, а значение сохраненных данных называется rvalue (l и r означает левое и правое).

Рассмотрим утверждение:

int x = 10;

Внутри программа связывает адрес памяти с переменной x. В этом случае предположим, что программа назначает x для размещения по адресу 1001 (не реалистичный адрес, а выбранный для простоты). Следовательно, lvalue (адрес памяти) x равен 1001, а значение r (значение данных) x равно 10.

Доступ к rvalue осуществляется с помощью переменной x. Для доступа к lvalue необходим "адрес" оператора ('&). Выражение "& x считывается как" адрес x ".

Expression          Value
----------------------------------
x                   10
&x                  1001

Значение, сохраненное в x, может быть изменено в любое время (например, x = 20), но адрес x (& x) никогда не может быть изменен.

Указатель - это просто переменная, которая может использоваться для изменения другой переменной. Он делает это, имея адрес памяти для своего rvalue. То есть, он указывает на другое место в памяти.

Создание указателя на "x" выполняется следующим образом:

int* xptr = &x;

"int *" сообщает компилятору, что мы создаем указатель на целочисленное значение. Часть "= & x" сообщает компилятору, что мы присваиваем адрес x rvalue xptr. Таким образом, мы сообщаем компилятору, что xptr "указывает на" x.

Предполагая, что xptr присваивается адресу памяти 1002, тогда память программ может выглядеть так:

Variable    lvalue    rvalue
--------------------------------------------
x           1001      10   
xptr        1002      1001

Следующий фрагмент головоломки - это "оператор направления" ('*), который используется следующим образом:

int y = *xptr;

Оператор косвенности сообщает программе интерпретировать rvalue xptr как адрес памяти, а не значение данных. То есть программа ищет значение данных (10), сохраненное по адресу, предоставленному xptr (1001).

Объединяя все это:

Expression      Value
--------------------------------------------
x                   10
&x                  1001
xptr                1001
&xptr               1002
*xptr               10

Теперь, когда понятия были объяснены, вот какой код демонстрирует силу указателей:

int x = 10;
int *xptr = &x;

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

*xptr = 20;

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

Для вывода вы увидите (Примечание: адрес памяти будет отличаться каждый раз):

x = 10
&x = 3537176
xptr = 3537176
*xptr = 10
x = 20
*xptr = 20

Обратите внимание, что присвоение значения '* xptr изменило значение' x. Это связано с тем, что '* xptr и' x относятся к одному и тому же местоположению в памяти, о чем свидетельствуют "& x и" xptr, имеющие одинаковое значение.

Ответ 3

В этой ссылке есть видео, описывающее, как работают указатели, с использованием глины. Информативно и легко усваивается.

Эта страница содержит некоторую полезную информацию об основных классах.

Ответ 4

У меня была проблема с пониманием указателей в паскале.:) Как только я начал делать ассемблерные указатели, это был единственный способ получить доступ к памяти, и это просто ударило меня. Это может показаться далеким, но попробовать ассемблер (который всегда является хорошей идеей, чтобы попытаться понять, что такое компьютеры на самом деле), вероятно, научит вас указателям. Классы - ну, я не понимаю вашу проблему - было ли ваше обучение чисто структурированным программированием? Класс - это просто логичный способ взглянуть на реальные модели жизни - вы пытаетесь решить проблему, которая может быть суммирована в ряде объектов/классов.

Ответ 5

Указатели и классы - это совершенно разные темы, поэтому я бы не собирался объединять их вместе. Из двух я бы сказал, что указатели более фундаментальны.

Хорошим упражнением для изучения того, что такое указатели, является следующее:

  • создать связанный список
  • итерации через него от начала до конца
  • отмените его так, чтобы теперь голова была спиной, а спина теперь голова

Сделайте все сначала на доске. Если вы можете сделать это легко, у вас не должно возникнуть проблем с пониманием того, что такое указатели.

Ответ 6

В других ответах указатели уже, кажется, адресованы (не каламбур).

Классы являются фундаментальными для OO. Я столкнулся с огромными проблемами, размахивая головой в ОО - вроде, десять лет неудачных попыток. Книга, которая наконец помогла мне, была Крейг Ларман "Применение UML и шаблонов". Я знаю, что это звучит так, как будто это что-то другое, но на самом деле это отличная работа по облегчению вас в мир классов и объектов.

Ответ 7

Мы просто обсуждали некоторые аспекты С++ и OO на обед, кто-то (великий инженер на самом деле) говорил, что, если у вас нет действительно сильного программирования, прежде чем вы изучите С++, это буквально испортит вам.

Я настоятельно рекомендую сначала изучить другой язык, а затем перейти на С++, когда вам это нужно. Не похоже, что в указателях есть что-то замечательное, это просто рудиментарный кусок, оставшийся после того, как было сложно для компилятора эффективно преобразовывать операции в сборку без них.

В наши дни, если компилятор не может оптимизировать операцию с массивом лучше, вы можете использовать указатели, ваш компилятор сломан.

Пожалуйста, не поймите меня неправильно, я не говорю, что С++ - это ужасно или что-то еще, и я не хочу начинать дискуссионную дискуссию, я использовал ее и использую ее иногда, я просто рекомендую вам начать с чем-то другим.

Это действительно НЕ нравится учиться управлять ручным автомобилем, а затем легко применять его к автоматическому, это больше похоже на обучение вождению на одном из этих огромных строительных кранов, тогда предполагая, что это будет применяться, когда вы начнете водить автомобиль- - тогда вы обнаружите, что ведете машину по середине улицы со скоростью 5 миль в час с включенными аварийными огнями.

[править] обзор последнего абзаца - я думаю, что это была моя самая точная аналогия!

Ответ 8

Для указателей:

Я нашел этот пост, очень задумчивое обсуждение указателей. Может быть, это поможет. Вы знакомы с рефренсами, такими как С#? Это то, что на самом деле относится к чему-то еще? Это, вероятно, хорошее начало для понимания указателей.

Кроме того, посмотрите на пост Кент Фредрик ниже по другому пути, чтобы представить себя указателям.

Ответ 9

Чтобы понять указатели, я не могу рекомендовать K & R книгу достаточно высоко.

Ответ 10

Изучите язык ассемблера, а затем узнайте C. Затем вы узнаете, каковы основные принципы машины (и указатели).

Указатели и классы являются фундаментальными аспектами С++. Если вы их не понимаете, значит, вы действительно не понимаете С++.

Лично я сдерживался на С++ в течение нескольких лет, пока не почувствовал, что у меня есть твердое понимание C и что происходит под капотом на ассемблере. Хотя это было довольно давно, я думаю, что это действительно помогло моей карьере понять, как компьютер работает на низком уровне.

Обучение программированию может занять много лет, но вы должны придерживаться его, потому что это очень полезная карьера.

Ответ 11

Нет никакой практики для практики.

Легко прочитать книгу или послушать лекцию и почувствовать, что вы следите за происходящим.

Я бы порекомендовал взять некоторые из примеров кода (я предполагаю, что у вас их есть на диске где-то), скомпилировать их и запустить, а затем попытаться изменить их, чтобы сделать что-то другое.

  • Добавить другой подкласс в иерархию
  • Добавить метод в существующий класс
  • Измените алгоритм, который выполняет итерацию вперед через коллекцию, чтобы пойти назад вместо.

Я не думаю, что есть какая-нибудь книга "серебряной пули", которая собирается это сделать.

Для меня, что привело домой то, что указатели означали, работало в сборке и считало, что указатель фактически был просто адресом, а указатель не означал, что на него указывал значимый объект.

Ответ 12

В случае классов у меня было три метода, которые действительно помогли мне сделать прыжок в реальном объектно-ориентированном программировании.

Сначала я работал над игровым проектом, который сильно использовал классы и объекты, с большим использованием обобщений (вид или отношения, например, ученик - это своего рода человек) и состав (has- отношения, например, у студента есть студенческий кредит). Разрушение этого кода заняло много работы, но действительно показало вещи в перспективе.

Вторая вещь, которая помогла, была в моем классе системного анализа, где мне приходилось составлять диаграммы классов UML http://www.agilemodeling.com/artifacts/classDiagram.htm" > . Это я действительно нашел, помог мне понять структура классов в программе.

Наконец, я помогаю учителям в моем колледже программировать. Все, что я могу сказать об этом, - это многому научиться, обучаясь и видя, как другие люди подходят к проблеме. Много раз студент будет пробовать вещи, о которых я никогда бы не подумал, но обычно имеет большой смысл, и у них просто проблемы, реализующие их идею.

Мое лучшее слово в совете - это много практики, и чем больше вы программируете, тем лучше вы это поймете.

Ответ 13

Притворите указатель адресом массива.

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

его графическое упрощение, но по большей части, пока вы никогда не хотите знать, что это за номер или его вручную, вы должны быть в порядке.

Назад, когда я понял C У меня было несколько макросов, которые у меня были, что более или менее разрешало вам использовать указатели, точно так же, как они были индексом массива в памяти. Но я давно потерял этот код и давно забыл.

Я помню, что он начинался с

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

и это само по себе вряд ли полезно. Надеюсь, кто-то еще может расширить эту логику.

Ответ 14

Лучшая книга, которую я читал по этим темам, - это мыслить на С++ Брюсом Эккелем. Вы можете скачать его бесплатно здесь.

Ответ 15

Для классов:

Для меня был момент, когда я узнал об интерфейсах. Идея абстрагироваться от деталей того, как вы написали, разрешила проблему и дала только список методов, которые взаимодействуют с классом, была очень проницательной.

Фактически, мой профессор прямо сказал нам, что он будет оценивать наши программы, подключая наши классы к его тестовому жгуту. Градация будет проводиться на основе требований, которые он дал нам, и была ли программа разбита.

Короче говоря, классы позволяют обернуть функциональность и называть ее более чистым способом (чаще всего всегда есть исключения)

Ответ 16

Книга, которая треснула указателями для меня, была Иллюстрируя Ansi C от Дональда Алкака. Его полная коробка и стрелочные диаграммы с рисованным рисунком, которые иллюстрируют указатели, арифметику указателей, массивы, строковые функции и т.д.

Очевидно, что это книга 'C', но для основных основ ее трудно превзойти

Ответ 17

Одна из вещей, которая действительно помогла мне понять эти концепции, - изучить UML - унифицированный язык моделирования. Видение концепций объектно-ориентированного дизайна в графическом формате действительно помогло мне узнать, что они означают. Иногда пытаться понять эти понятия, просто взглянув на то, какой исходный код их реализует, может быть трудно понять.

Наблюдение объектно-ориентированных парадигм как наследование в графической форме - очень мощный способ понять концепцию.

Мартин Фаулер UML Distilled - хорошее, краткое введение.

Ответ 18

От ответа lassevek на похожий вопрос на SO:

Указатели - это концепция, которая для многих может сбивать с толку сначала, в особенно когда дело доходит до копирования значения указателя вокруг и все еще ссылаясь на тот же блок памяти.

Я нашел, что лучшая аналогия заключается в том, чтобы рассмотрите указатель как часть бумагу с домашним адресом на ней и блок памяти, на который он ссылается, как фактический дом. Все виды операций может быть легко объяснено:

  • Скопировать значение указателя, просто написать адрес на новом листе бумаги
  • Связанные списки, листок бумаги в доме с адресом следующего дом на нем
  • Освобождение памяти, уничтожение дома и удаление адреса.
  • Утечка памяти, вы теряете лист бумаги и не можете найти дом.
  • Освобождение памяти, но сохранение (теперь недействительной) ссылки, снести дом, удалите одну из частей бумага, но есть еще один лист бумаги со старым адресом на нем, когда вы перейдите по адресу, вы не найдете дом, но вы можете найти что-то который напоминает руины одного
  • Переполнение буфера, вы перемещаете больше вещей в дом, чем можете возможно, вписывается в соседний дом

Ответ 19

Чтобы лучше понять указатели, я думаю, может быть полезно посмотреть, как работает язык ассемблера с указателями. Концепция указателей на самом деле является одной из основных частей языка ассемблера и архитектуры команд процессора x86. Возможно, это позволит вам упасть, поскольку указатели являются естественной частью программы.

Что касается классов, помимо парадигмы OO, я думаю, что может быть интересно посмотреть классы с низкоуровневой двоичной перспективы. Они не настолько сложны в этом отношении на базовом уровне.

Вы можете прочитать Внутри объектной модели С++, если хотите лучше понять, что находится под объектной моделью С++.

Ответ 20

Классы относительно легко понять; ООП может занять много лет. Лично я не полностью понял истинный ООП до прошлого года. Очень плохо, что Smalltalk не так широко распространен в колледжах, как и должно быть. Это действительно приводит к тому, что ООП - это объекты, торгующие сообщениями, а не классы, являющиеся автономными глобальными переменными с функциями.

Если вы действительно новичок в классах, то концепция может занять некоторое время. Когда я впервые встретил их в 10-м классе, я не получил его, пока у меня не был кто-то, кто знал, что они делают, выполняя код и объясняя, что происходит. Это то, что я предлагаю вам попробовать.

Ответ 21

Точка, в которой у меня действительно были указатели, была кодировка TurboPascal на FatMac (около 1984 года или около того) - в то время это был родной язык Mac.

У Mac была нечетная модель памяти, при которой при распределении адреса память хранилась в указателе на куче, но ее местоположение не гарантировалось, и вместо этого процедуры обработки памяти возвращали указатель на указатель, как ручка. Следовательно, для доступа к любой части выделенной памяти необходимо было разыменовать дескриптор дважды. Это заняло некоторое время, но постоянная практика в конечном итоге привела урок домой.

Обработка указателя на Pascal легче понять, чем С++, где синтаксис не помогает новичкам. Если вы действительно и действительно застряли в указателях понимания в C, то лучшим вариантом может быть получение копии aa Pascal-компилятора и попытка написать в нем некоторый базовый код указателя (Pascal достаточно близок к C, вы получите основы в течение нескольких часов). Связанные списки и тому подобное были бы хорошим выбором. Как только вам станет комфортно возвращаться на С++, и с освоенными концепциями вы обнаружите, что скала не будет выглядеть такой крутой.

Ответ 22

В некотором смысле вы можете считать, что "указатели" являются одним из двух основных типов программного обеспечения, другие - "значения" (или "данные" ) - которые существуют в огромном блоке уникально адресуемых мест памяти, Думаю об этом. Объекты и структуры и т.д. В действительности не существуют, только значения и указатели. Фактически, указатель также является значением... значение адреса памяти, которое, в свою очередь, содержит другое значение.... и т.д.

Итак, в C/С++, когда вы объявляете "int" (intA), вы определяете 32-битный кусок памяти, который содержит значение - число. Если вы затем объявите "int pointer" (intB), вы определяете 32-битный кусок памяти, который содержит адрес int. Я могу назначить последнее, чтобы указать на первое, указав "intB = & intA", и теперь 32 бита памяти, определенные как intB, содержат адрес, соответствующий местоположению intA в памяти.

Когда вы "разыскиваете" указатель intB, вы смотрите на адрес, хранящийся в памяти intB, и обнаруживаете это местоположение, а затем просматриваете значение, хранящееся там (число).

Как правило, я столкнулся с путаницей, когда люди теряют контроль над тем, с чем они имеют дело, поскольку они используют операторы "&", "*" и "- > " - это адрес, значение или что? Вам просто нужно сосредоточиться на том, что адреса памяти - это просто местоположения, и эти значения являются двоичной информацией, хранящейся там.

Ответ 23

Вы читали Bjarne Stroustrup Язык программирования С++? Он создал С++.

С++ FAQ Lite также хорош.

Ответ 24

Для указателей и классов, вот моя аналогия. Я буду использовать колоду карт. Колода карт имеет номинал и тип (9 сердец, 4 лопат и т.д.). Итак, на нашем С++, как и на языке программирования "Deck of Cards", мы скажем следующее:

HeartCard card = 4; // 4 of hearts!

Теперь вы знаете, где 4 сердца - это потому, что по golly вы держите колоду, лицом вверх в руке, и она наверху! Поэтому по отношению к остальным картам мы просто скажем, что 4 сердца находятся в НАЧАЛЕ. Итак, если бы я спросил, какая карта находится НА НАЧАЛЕ, вы бы сказали: "Четыре сердца, конечно!". Ну, ты просто "указал" мне, где находится карта. На нашем языке программирования "Deck of Cards" вы также можете сказать следующее:

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

Теперь поверните свою колоду карт. На обратной стороне теперь НАЧИНАЕТСЯ, и вы не знаете, что такое карта. Но, допустим, вы можете сделать все, что захотите, потому что вы полны волшебства. Давайте сделаем это в нашей "Палубе карт" langauge!

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 сердец! Вы поворачиваете свою карточку и, вуаля! Теперь * может вас отбросить. Если это так, проверьте это:

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. Мы знаем, что такое HeartCard... Это карта с ценностью и типом сердца! Итак, мы классифицировали это как HeartCard. Каждый язык имеет аналогичный способ определения или "классификации" того, что вы хотите, но все они имеют одну и ту же концепцию! Надеюсь, это помогло...

Ответ 25

Вы можете найти эту статью Joel поучительной. В стороне, если вы "работали на С++ в течение некоторого времени" и получили высшее образование в CS, возможно, вы отправились в JavaSchool (я бы утвердил, что вы вообще не работали на С++; работал на C, но с использованием компилятора С++).

Кроме того, только чтобы ответить на вопросы hojou и nsanders, указатели очень важны для С++. Если вы не понимаете указателей, то вы не понимаете основ С++ (признавая, что это факт является началом понимания С++, кстати). Точно так же, если вы не понимаете классы, то вы не понимаете основ С++ (или OO, если на то пошло).

Для указателей, я думаю, что рисование с помощью ящиков - прекрасная идея, но работа в сборке также является хорошей идеей. Любые инструкции, которые используют относительную адресацию, помогут вам понять, какие указатели довольно быстро, я думаю.

Что касается классов (и, в общем, объектно-ориентированного программирования), я бы рекомендовал последнее издание Stroustrups "The С++ Programming Language". Мало того, что это канонический справочный материал на С++, но он также имеет довольно много материала для многих других вещей: от базовых объектно-ориентированных иерархий классов и наследования вплоть до разработки принципов в больших системах. Это очень хорошее чтение (если не немного толстое и красное в пятнах).

Ответ 26

Указатели не являются своего рода волшебным материалом, вы используете их все время!
Когда вы говорите:

int a;

и компилятор генерирует хранилище для 'a', вы фактически говорите, что вы объявляете int и вы хотите назвать его ячейку памяти 'a'.

Когда вы скажете:

int * a;

вы объявляете переменную, которая может содержать ячейку памяти для int. Это так просто. Кроме того, не пугайтесь арифметики указателей, просто всегда иметь в виду "карту памяти", когда вы имеете дело с указателями и думаете в терминах прохода по адресам памяти.

Классы на С++ - это всего лишь один из способов определения абстрактных типов данных. Я бы предложил прочитать хорошую книгу ООП, чтобы понять концепцию, а затем, если вам интересно, узнайте, как компиляторы С++ генерируют код для имитации ООП. Но это знание придет вовремя, если вы будете придерживаться С++ достаточно долго:)

Ответ 27

Ваша проблема, похоже, является ядром C в С++, а не С++. Получите себя Керниган и Ричи (язык программирования C). Вдохните. Это очень хороший материал, один из лучших книг по языку программирования, когда-либо написанных.