@zus

ClassName::functionName или objectName.functionName?

Привет всем!
Изучаю C++ и собственно возник вопрос.
Почему иногда мы используем конструкию типа ClassName::functionName , а иногда objectName.functionName ? Т.е. к примеру в файле с main указываем второй вариант, а в файле с логикой функций используем первый вариант? От чего это зависит.

Я понимаю что cout << objectName.functionName означает, что мы выводим возвращаемые данные функции , которая принадлежит объекту класса(оперирует данными объекта).
Но вот почему мы иногда использует cout << ClassName::functionName вместо первого варианта я не могу понять...

Заранее благодарен!
  • Вопрос задан
  • 211 просмотров
Пригласить эксперта
Ответы на вопрос 3
Nipheris
@Nipheris Куратор тега C++
Отвечу без догадок.
> Я понимаю что cout << objectName.functionName означает, что мы выводим возвращаемые данные функции , которая принадлежит объекту класса(оперирует данными объекта).
Это хорошо соответствует истине, добавить в общем-то нечего.

> Но вот почему мы иногда использует cout << ClassName::functionName вместо первого варианта я не могу понять...
"::" это оператор расширения области видимости. В языке C++ различные сущности могут быть объявлены "внутри" других сущностей. Например, функции внутри классов, как в вашем случае - тогда они становятся методами. Классы, функции и переменные могут объявляться внутри namespace-ов. Классы могут объявляться внутри других классов. Классы даже могут объявляться внутри определения функций.
Во всех этих и других случаях, можно говорить о двух именах сущности - коротком, которое уникально в рамках родительской сущности, и о полном, по которому к сущности можно обратиться из любого места в программе. Например,

namespace MyLibrary {
    namespace UI {
        class Widget {
         ///
        };
        class Controller {
        private:
            Widget *widget; // Здесь Widget будет видно по короткому имени
        };
    }
}

namespace App {
  MyLibrary::UI::Widget *w; // А вот здесь уже нужно использовать полное
}

Это правило касается всего - и классов, и функций, и переменных. И вот как раз для построения полного имени и нужен оператор "::". Грубо говоря, он "открывает" указанную вами сущность и после него вы можете указать имя вложенной сущности. MyLibrary::UI::Widget - открыли неймспейс MyLibrary, в нем взяли и открыли неймспейс UI, в нем взяли сущность Widget. В нашем случае Widget это класс, поэтому мы можем использовать его в качестве типа, например объявить указатель на его объект. Если бы у нас была переменная mainWidget в том же UI, мы бы могли написать так:
MyLibrary::UI::mainWidget = new MyLibrary::UI::Widget(); // создали объект виджета в куче и поместили указатель на него в переменную-указатель


С точкой тут вот в чем дело. По умолчанию функции внутри классов считаются методами ОБЪЕКТА (экземпляра этого класса), и, как вы правильно сказали, оперируют с данными объекта, а значит они НЕ МОГУТ быть вызваны без указания того объекта. Иными словами, они не самостоятельны, для их вызова всегда нужно указывать объект, с которым метод будет работать. Можете представлять себе, что у всех методов экземпляра есть неявный параметр this, который, хоть и не пишется в списке, тем не менее всегда присутствует, и его значение надо задавать. Оператор точка - это и есть способ задать значение "this" - т.е. указать, для какого объекта вызывается метод.

Совсем другое дело - static-методы. По сути это самостоятельные функции, просто объявлены внутри класса, и имеют доступ к его private-сущностям. Поэтому для их вызова достаточно указать полное имя, используя оператор "::", например Widget::create или MyLibrary::UI:Widget::create, что в принципе одно и то же. Более полная запись нужна, когда вы находитесь в другой области видимости, и просто "не видите" нужный идентификатор. Или же если у вас в текущий области НЕСКОЛЬКО идентификаторов с одинаковым именем (конфликт имен), и компилятору необходимо однозначно понять, о какой сущности вы говорите.

Касательно случая, который вам непонятен - про использование :: в составном имени метода - тут все просто. Т.к. вы собираетесь не ВЫЗВАТЬ метод, а дать его ОПРЕДЕЛЕНИЕ, вам не нужна точка, т.к. точка это синтаксис вызова метода для конкретного объекта. Вам лишь нужно построить ПОЛНОЕ имя для метода, находящегося внутри класса, чтобы компилятор понял, ЧЕЙ код вы будете писать в фигурных скобках. А полное имя строится с помощью двоеточия, поэтому и получается ClassName::methodName { код метода }. Просто methodName вы написать не можете, т.к. в cpp-файле вы находитесь УЖЕ НЕ ВНУТРИ определения КЛАССА, и компилятор посчитает, что вы объявляете и описываете совсем другую, свободную функцию methodName, совершенно не имеющую отношения к методу methodName в классе ClassName.

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

Если остались вопросы - задавайте. Запомнить проще всего так: точка - это обращение к члену объекта структуры или вызов метода (т.е. слева от точки всегда стоит объект), а "::" - это способ составления ИМЕНИ какого-либо элемента вашей программы - класса, функции, метода, переменной и т.д.
Ответ написан
tlito
@tlito
drupal, c++, seo
через точку - это обращение к функции объекта.
а через двойное двоеточие - это указание области видимости кажись.
например
std::cout << "hi";
вот тут указание области определения функции, ну или класса std. наверное это есть 1 такой объект класса std.
но это все догадки.
Ответ написан
Комментировать
AtomKrieg
@AtomKrieg
Давай я поищу в Google за тебя
ClassName::functionName , а иногда objectName.functionName

Это вопрос про статик методы? Рассматривайте статик метод класса как функцию в пространстве имен имени класса.

#include <iostream>
using namespace std;

class Test
{
public:
	static void st() { cout << "static" << endl; }
	void nonst() {cout << "non static" << endl; }
};

int main(int argc, char **argv)
{
	Test t;

	Test::st(); // для статик метода можно так
	t.st(); //и так тоже можно

	Test::nonst(); // compilation error: так нельзя для не статик метода
	t.nonst();  //можно только так

	system("pause");
	return 0;
}
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы