Wskaźnik do składowej klasy będącej tablicą

0

Weźmy taki prosty program:

class A
{	public:
		int n;
		int tab[10];
};


int main()
{	int A::*wsk1 = &A::n;
	//int A::*wsk2 = A::tab;
	return 0;
}

To kompiluje się bezproblemowo, ale odkomentowanie linii

	//int A::*wsk2 = A::tab;

powoduje błąd kompilacji. Z tego co wiem powinno się dać pokazywać na tę tablicę, czy mógłby ktoś podpowiedzieć jak?

0

int A::*wsk2 to znaczy:
wsk2 jest wskaźnikiem do pokazywania wewnątrz klasy A na obiektu typu int
A ty przecież chcesz pokazać na tablicę a nie na jednego inta. Po prostu źle ten wskaźnik napisałeś.

0

przemyśl swoja odpowiedz jeszcze raz, Shalom. Blad autora tkwi tylko w tym, ze przy A::tab zapomnial &

zarowno:
int A::*wsk2 = &A::tab; //edit <------------- FAIL, ta linia jest nieprawidlowa (czemu? czytaj dalej)
int (A::*wsk2)[10] = &A::tab;
sa poprawne i roznia sie tylko tym, czy z punktu widzenia wskaznika tablica jest sztywnoelementowa czy nie.

0

@quetzalcoatl masz rację (choć nie do końca, o czym dalej), ale uważam że ja pośrednio też ją mam. Ja tylko przeczytałem ten wskaźnik. Poza tym nadal uważam że jest on źle napisany. Wersja:

int (A::*wsk)[10] = &A::tab;

czyli: wsk jest wskaźnikiem do pokazywania wewnątrz klasy A na obiekty typu 10-elementowa tablica intów
jest moim zdaniem tą poprawną.
Taka wersja "int A::*wsk2 = &A::tab;" ci się w ogóle kompiluje? Ta wersja jest lekko nielogiczna, bo jakiego typu jest tutaj w takim razie "tab"?

Rozłóżmy to na czynniki pierwsze (bo może ja zwyczajnie coś przeoczyłem :) )
int A::*wsk2 = &A::tab
po lewej mamy wskaźnik, po prawej stronie pobieramy adres który przekazujemy temu wskaźnikowi
po lewej mamy wskaźnik na inta, wiec po prawej powinniśmy pobrać adres inta
z tego wynika że "tab" musiałoby być intem, a nie jest, bo jest tablicą intów, ew można by uznać że to wskaźnik do inta.
Konkludując jeśli w ogóle to musiałoby to być:

int* A::*wsk;

Ale g++ też pozwoli na przypisanie temu wskaźnikowi adresu tablicy o statycznym rozmiarze.

Brak ampersanda to raczej przeoczenie autora jak i moje, bo oczywiście jak mamy wskaźnik to trzeba mu podać jakis adres.

Oczywiście zdaje sobie sprawę z tego ze skillem w C/C++ zjadasz mnie na śniadanie ;) Więc zakładam taką możliwość że zwyczajnie zjadłeś tam tą jedną gwiazdkę ;)

0

Hallo @all!

A. Operator "::" jest to tzw. "Scope Resolution Operator" ktory umozliwia dostep do zmiennych o tej samej nazwie wystepujacych na roznych poziomach programu (patrz przyklad).

Nie rozumiem deklaracji zmiennych wsk1 i wsk2. Dlatego prosze o wyjasnienie ich sensu.

int main()
{        int A::*wsk1 = &A::n;
        //int A::*wsk2 = A::tab;
        return 0;
}

B. Na moj chlopski rozumek, aby wyjasnic sprawe wskaznikow w klasie A, powinno sie stworzyc obiekt takiej klasy:

#include "stdafx.h"
#include <iostream>
using namespace std;

class A
{
	public:
                int n;
                int tab[10];
};

int _tmain(int argc, _TCHAR* argv[])
{
	A *ObjektA = new A();

	ObjektA->n = 15;
	ObjektA->tab[0] = 27;

	int  *wsk1 = &ObjektA->n;
	int  *wsk2 = ObjektA->tab;

	cout << &ObjektA->n   << "   " << ObjektA->n << "\n";
	cout << wsk1 << "   " << *wsk1 << "\n";

	cout << ObjektA->tab  << "   " << ObjektA->tab[0] << "\n";	
	cout << wsk2 << "   " << *wsk2 << "\n";

	system("Pause");

	return 0;
}

Czy moze zupelnie czegos nie zrozumialem?

Pozdrawiam
Markus

0

Shalom: przynam sie od razu, ani mojej linijki #1 ani linijka #2 nie sprawdzilem, tylko napisalem jak leci. Teraz defacto troche mi sie tez nie chce, ale skoro jest watpliwosc to sprawdzilem - tylko druga jest uznawana za poprawna przez mscv2008. Błąd przy tej która Tobie nie pasuje wynika nie z powodu skladni, ale z powodu niedozwolonej kowersji typow, ktora przy member-pointerach jest bardziej restrykcyjna.

struct X{ int ar[10]; };

int main()
{
    //int (X::* ptr) = &X::ar;		// moj fail - niemozliwe
    int (X::* ptr2)[10] = &X::ar;

    X a;
    int (* ptr3) = a.ar;			// autokonwersja int[] -> int*
    int (* ptr4)[10] = &a.ar;
}

piszac na szybko, zasugerowalem sie przypadkiem 'ptr3'. masz racje, ze w tym przypadku musialoby to wygladac jak intX:: -- ale w tylko w przypadku tablicy dynamicznej! int[10] zawarta w X jest statyczna, wiec co najwyzej moj zapis mogl byc poprawny. nie jest - gdyz nie mozna membera int[10] potraktowac jako serii memberow typu int ---- a moj zapis to dokladnie by oznaczal

0

Wszystko pięknie, ale autorowi wątku chodzi raczej o to by mieć wskaźnik do int wewnątrz klasy A, który wskazuje na element tablicy będącej wewnątrz tej klasy.
Czyli autor nie ma problemu z lewą częścią wyrażenia (jak poprawia quetzalcoatl), ale z prawą, bo może chcieć wskaźnika na 3 element tej tablicy a nie na 0.
Na gcc próbowałem static_cast i nie zadziałało (inne rzutowania byłyby jak strasznie topornym rozwiązaniem).

0

@MarkusB/pytanie A
:: dokladnie tak sie nazywa, ale uzywa sie go w wielu roznych miejscach.
jedno z nich to "chodzenie" po namespaceach,
inne, to "chodzenie" po skladnikach klasy

namespace A
{
  namespace B
  {
    int zmienna;
    class C
    {
      int pole;
      class D { int pole2; };
    };
  }
}

pelna nazwa zmiennej globalnej: A::zmienna
pelna nazwa pola klasy: A::pole
pelna nazwa pola innerklasy: A::pole

jezeli teraz chcesz w dowolnym miejscu kodu powiedziec, niech zmienna globalna wynosi 5, zapiszesz to:

A::B::zmienna = 5;
B::zmienna = 5;
zmienna = 5;

w zaleznosci w ktorym miejscu aktualnie "się" znajdujesz względem struktury namespaceow.

podobnie, jezeli "jestes" poza klasa, jakos musisz wskazac że chodzi Ci po 'pole' z klasy C a nie D, zapisy:

&C::pole
&D::pole

powinny więc być dla Ciebie w miare oczywiste

teraz, "luźnolatający" wskaznik na int, to jest po prostu int*
wskaznik na cos-w-klasie nie jest "luźnolatający", poniewaz aby go uzyc musisz podac instancje klasy. obiekt:

int a = 5;
int b = 5;

int* wskaznik = &a;
.....
int cede = *wskanzik; //odczyta zawartosc 'a', nie 'b'

class Klasa{int POLE, POLEINNE;};

Klasa a = .....;
Klasa b = .....;

???? wskaznik = &Klasa::POLEINNE; // ustawiamy wskaznik na pole 'POLEINNE' klasy.
.....
//cede = *wskaznik; -- nie mozna! z czego mial by ten wskaznik odczytac? z obiektu A czy obiektu B?

cede = a .* wskaznik;   //bierzemy obiekt, a potem na nim wywolujemy wskanzik-na-pole. odczyta zawartosc z 'a', nie 'b'

jak sie zastanowic, roznica uzycia wskaznika-na-zawartosc oraz zwyklego wskaznika robi sie w miare sensowna. specjalnie w kodzie powyzej napisalem ????? zamiast typu wskaznika. roznica w nich oznacza, ze ????? to nie jest int*. jak wiec zapisac ze wskaznik ma byc na-int, ale ze dodatkowo ma byc na-cos-w-klasie-Klasa?

odpowiedz brzmi: do definicji wskaznika trzeba dolaczyc operator zakresu, ::

Co WCzym::* zmienna; kontra Co * zmienna;

z namespaceow pamietasz pewnie, ze "topowy" namespace to puste ::, tutaj tez to NIE funkcjonuje, ale mozesz tak na to patrzec:

Co WCzym::* zmienna; kontra Co ::* zmienna;

prawa wersja mowi, ze wskazywane "co" jest na samej gorze, nie jest opakowane
lewa wersja mowi, ze "Co" ma byc opakowane przez WCzym

specjalny zapis " WCzym::* " to wskaznik-w-opakowanie.
z namespaceami:

int (A::* zmienna) = &A::pole ---- adres pola w klasie D w klasie C w namespace A::B

niestety, skladnia C++ jest jesli chodzi o wskazniki totalnie kontra-intuicyjna. tak samo jak zapis wskaznikow na zlozone tablice czy funkcje jest wrogi, tak tutaj jest tylko troszke lzej.. moze i pieknie by bylo, gdybysmy kiedys mogli napisac: A::int* zmienna = &A::pole;

... ale wtedy skad kompilator by wiedzial, ze chodzi o TEN int, a nie o nasza klase ktora siedzi w A:: i nazywa sie 'int'? nie daloby sie. kolejna proponowalna 'lepsza' skladnia to np. A::(std::int)* zmienna = &A::pole; ale szczerze, ja juz przywyklem do obecnej i juz mnie malo rzeczy razi (jak sobie rozważę alternatywy:) )

0
MarekR22 napisał(a)

Wszystko pięknie, ale autorowi wątku chodzi raczej o to by mieć wskaźnik do int wewnątrz klasy A, który wskazuje na element tablicy będącej wewnątrz tej klasy.
Czyli autor nie ma problemu z lewą częścią wyrażenia (jak poprawia quetzalcoatl), ale z prawą, bo może chcieć wskaźnika na 3 element tej tablicy a nie na 0.
Na gcc próbowałem static_cast i nie zadziałało (inne rzutowania byłyby jak strasznie topornym rozwiązaniem).

autor wątku powiedzial:

Z tego co wiem powinno się dać pokazywać na tę tablicę, czy mógłby ktoś podpowiedzieć jak
wiec dostał odpowiedzi takie.. zapytalby o pokazywanie na jej zawartosc, dostalby inne.

pokazywać na n-ty element tablicy mozna, a jakze - przez zwykly int*, tylko trzeba miec instancje obiektu

class Klasa{int tab[10];}

Klasa x;
int * ptr = &x.tab;
*(ptr+4) = 666;

Mowienie o wskazniku na element tablicy luznolatajacej, a o wskazniku na element tablicy nalezacej do obiektu klasy, nie rozni sie absolutnie niczym.

tyci trick w tym, ze:

  • jesli mowimy o tablicy - mowimy o wnetrzy obiektu lub wnetrzu klasy
  • jesli mowa o elemencie tablicy to mowimy juz o wnetrzu istniejacego obiektu klasy

Tak jak napisalem w poscie do Shaloma:

  • majac luznolatajaca tablice Elem[], z racji C i temu podobnych, mozna traktowac jej elementy osobno jako osobne byty i brac do nich Elem*, autokonwersja ptr istnieje
  • nieluznolatajace, pole int klasy oraz element int pola tablicowego klasy nie sa rownoznaczne. nieistnieje autokonwersja (&X::Elem[])+i -> X::Elem*. w przeciwienstwie do int*, zapis X::int* oznacza membera klasy. element i-ty tablicy bedacej memberem klasy nie jest memberem klasy. jak widziales w moim pierwszym poscie, tez mnie to zdziwilo. pewnie niedopatrzenie, albo wzgledy alignu..

prosty "dowód" tego o czym mowie:

struct Y{ int a,b,c; int d[10]; };

Y y;
int Y::* p = &Y::a;

y.*p = 1;
++p;            // co to mialoby byc? przejscie na 'B' ?? obiekt to nie tablica. jesli chcemy grzebac w nim na zywca, to nie przez member pointer, tylko raw pointer..
y.*p = 2;

dlatego wlasnie elementow Y::d nie da sie uzywac jako "int Y::" --- bo mimo podobienstwa zapisu, to nie to samo. elementy tablicy D to int

1 użytkowników online, w tym zalogowanych: 0, gości: 1