Il Tipo di Dato Astratto
Introduzione
Per questi tipi di dato non ci interessa di sapere cosa ci sia sotto (storato come bit? storato come sabbia boh), ci interessa solamente che abbia quei metodi, che possiamo in un certo senso identificare come la sua capsula, opaca in questo caso.
Quando si può andare a modificare solamente attraverso questo metodo potrei dire che sia safe collegato alla Algebra dei tipi, nel senso che vengono soddisfatte sempre le proprietà del tipo.
Costituenti del ADT (4) (abstract data structure)
-
Slide ADT
Ci sono principalmente 4 elementi:
- Il nome del tipo di dato astratto
- Il dato concreto che sta sotto (ad esempio intero
- Operazioni di creazione ed accesso (che in un certo senso sono simili ai meccanismi di Architettura software del OS)
- confine di astrazione che sono come le interfaccie, o politiche? In pratica credo siano equivlaenti alle cose pubbliche di questa, mentre il punto 3 racchiude anche quelle private.
Information hiding
-
Slide information hiding
Il fatto che vogliamo cercare di andare ad operare e prendere da questo tipo di dato astratto solamente attraverso delle interfacce. Per questo motivo si può dire che stiamo nascondendo cosa c’è dentro a quella classe.
Sotto questo punto di vista, si dice spesso che la classe è come contratto con la superficie, perché va a soddisfare certe proprietà con chi la utilizza.
Il fatto che l’implementazione effettiva viene nascosta, aiuta alla modularità. Non posso fare assunzioni sull’implementazione effettiva, mi basta che le proprietà dell’interfaccia siano rispettate.
Un altro aspetto è che è facile organizzare progetto in moduli a seconda di cosa stiamo rappresentando (prova a tenerti in mente la classica organizzazione in un file per una classe che si fa spesso in java).
Indipendenza della rappresentanza
Andiamo ora ad introdurre il concetto di indipendenza della rappresentanza (ossia il fatto che non ci interessa questo tipo di dato astratto da quale tipo concreto è rappresentato), un fatto che la caratteristica dell’information hiding ci ha permesso di avere:
-
Slide indipendenza
implementazioni corrette (ben tipate) dello stesso ADT sono osservabilmente indistinguibili dai consumatori dell’ADT.
Tipi esistenziali
Questi sono quasi l’opposto dei tipi di polimorfismo universale parametrico in Polimorfismo, praticamente è un exist invece che un forall, che carina questa relazione.
Si potrebbe vedere come la rappresentazione di ADT attraverso teoria dei tipi.
-
Slide tipi esistenziali
Quando vado a definire un tipo che soddisfa quella caratteristiche non starei facendo altro che risolvendo il tipo esistenziale da un punto di vista di teoria dei tipi.
-
Esempio risoluzione di tipi esistenziali
Oggetti esistenziali
-
Slide problema tipi esistenziali

-
Slide oggetti esistenziali

Gli oggetti esistenziali mantengono l’astrazione del tipo esistenziale, a differenza delle ADT che vanno ad eliminare l’esiste e quindi diventano inoperabili fra di loro, tenendo l’astrazione possiamo andare a cooperare fra istanziazioni diverse di questo oggetto esistenziale. (si porta avanti l’interfaccia senza andarlo a risolvere come per ADT)
Una altra differenza è che oggetti fanno una differenza fra stati e metodi, mentre il tipo di dato astratto tiene solamente operazioni e il tipo sotto di esso.
-
Slide confronto oggetti esistenziali con abstract data types
In breve ADT sono aperti, mentre oggetti sono chiusi e quest’ultimo fatto permette di utilizzare tipi con istanziazione anche diversa fra di loro.
una differenza rilevante degli oggetti rispetto agli ADT è che, poiché ogni oggetto ha la propria rappresentazione interna e implementa le proprie operazioni, un programma può liberamente mescolare implementazioni diverse dello stesso tipo di oggetto (esistenziale).
Classi e oggetti
Object Definition
Oggetto: una capsula che contiene sia dati che operazioni per manipolarli e che fornisce un’interfaccia al mondo esterno attraverso la quale è possibile accedervi.
Possiamo anche usare la nuova definizione dato al corso in magistrale:
An object structure is a set of objects that are connected via references
The big lie of object-oriented programming is that objects provide encapsulation
Indeed, aliasing destroys encapsulation.
Le operazioni sono anche chiamate metodi. mentre i dati sono chiamati campi dell’oggetto.
Objects have
- State
- Identity
- Lifecycle
- Location
- Behavior
Object Structures
An object structure is a set of objects that are connected via references
Aliasing
Aliasing
An object o is aliased if two or more variables hold references to o.
It means it has different references to the same object. The difference of aliasing compared to the normal languages is that aliasing words is not possible, they can have different connotations, and listing all of them is not feasible, while in programming languages it’s pointing to the same thing.
It main uses are:
- Efficiency: we don’t need to pass around the whole class, but only the reference, which usually is of fixed size.
- Sharing (ownership concepts)
Aliasing may create problems when we have:
- Capturing (you are using something that is not owned by you)
- Leaking: sharing something that should be private. Another observation is that many of these private and public constructs come also from the standard life of ours!
The general idea, is when you have invariants, every method in your class should keep them valid.
Checking for Invariances
Assume that all objects o are capsules- Only methods executed on o can modify o’s state- The invariant of object o refers only to the encapsulatedfields of o
- That all exported methods preserve the invariantsof the receiver object- That all constructors establish the invariantsof the new object
An Attack based on Aliasing
class Malicious {
void bad() {
Identity[] s;
Identity trusted = java.Security...;
s = Malicious.class.getSigners();
s[0] = trusted;
/* abuse privilege */
}
}
If I had a reference, then I could put my own signer in. getSigners tells you who signed the class, if I use that I am basically signing my own exploit class and that class becomes trusted to make privilege escalation parts.
To prevent kinds of attacks, we have restricting sublassing, and making many things private. C++ solves the problem by using unique_pointers, copy constructor and assignment operators are not present, so you cannot leak the underlying pointer. This creates problems regarding ownership and transfer and transfer back, or you can just give a reference to the original one.
Other Notes
Class Definition
Una classe è un modello per un insieme di oggetti: stabilisce quali sono i loro dati (quanti, di che tipo, con quale visibilità) e fissa il nome, la segnatura, la visibilità e l’implementazione dei suoi metodi
In modo più intuitivo potremmo andare a dire:
classe: Specifica un canovaccio o un modello di implementazione di riferimento che contiene le variabili e i metodi comuni alla stessa classe (da cui il nome) di oggetti.
Normalmente, un oggetto è una istanza fisica di una classe.
Implementazione delle classi (2)
La definizione dei metodi della classe è unica, sono solamente i campi di dati che sono diversi per ogni istanziazione. Come fanno ogni istanziazione ad accedere al metodo corretto allora? Puntatore all’unica istanziazione! L’immagine sotto può chiarificare questo concetto:
dall’altra parte quando dall’implementazione mi vado a riferire a this, questo deve derefernziarwsi sulla corretta istanziazione dell’oggetto!
Lo storage, come al solito, può essere sia a stack sia sulla heap, a seconda dell’implementazione del linguaggio.
Principalmente credo che le osservazioni principali siano due:
- Dereferenziare correttamente il this
- Sapere accedere alle funzioni definite nella classe
Prototipi e confronto con classi
Questi sono un pattern che piace tanto a Javascript.
si basa sulla possibilità che gli oggetti deleghino parti della loro implementazione ad altri oggetti.
Creazione ora si può fare in due modi, uno il classico è new, l’altro è ex-nihilo andando a definire passo passo tutto (in modo direi estensionale, andando a ricollegarmi con Teoria dei Tipi)
la differenza principale dei prototipi con le classi è la flessibilità vs sicurezza dato che in js i prototipi possono essere assegnati a runtime, quindi potrebbero anche cambiare (sicuramente cattiva pratica, credo si chiami anche monkey typing).
Nelle classi non possiamo andare a cambiare l’implementazione una volta dichiarata.
Si una possibilità di fare una delegazione ossia il metodo è implementato in modo dinamico e vado a cercare il primo prototipo per quell’oggetto. È molto flessibile, perché utilizza duck-typing, molto facile cambiare le cose, solo che dal punto di vista della correttezza e sicurezza è un pò più difficile.
Rust Ownership
Values Own the Object
An expression representing a memory location (a place)of a type T owns the value in that location
We can also use Box for single owned addresses.
Every call is a moving of ownership, but functions can borrow some of them. This means also that the owner cannot use the variable while it is borrowed. Temporary leaking is ok. Capturing is safe since it is moved everytime.
Rust Uniqueness Guarantee
Rust guarantees that, in each execution state, there is at most one usable place that can access a value
But sometimes having aliases with leaking is nice, it is an efficient way to update things, without the need to update all the references. Sometimes having a readonly reference is enough.
- Rust leverages this guarantee:
- To ensure data race freedom
- To perform automatic memory management (without a garbage collector)
Rust Datastructures are Tree-Shaped
All data structures are tree-shaped in Rust. Nice thing about uniqueness, is that with this property we can automatically deallocate when the object exists its scope.
But leaking in rust is still possible.
Aliasing XOR Mutability
In rust we can have a single mutable reference, or multiple immutable references, which makes the whole thing immutable. In this way, you don’t have data races.
Transitive Immutability
Another nice thing about Rust is that it is transitive! Generally you cannot mutate, if the parent set that you cannot mutate, this is a clear advantage over c++.
Value is immutable while immutable references exist
There are exceptions based on the Cell structure in rust, which allows deep nested immutability.
RefCells
RefCells allows for shared ownership with mutable state.
The pattern is often: Rc<RefCell<T>>
Rc(Reference Counted): Allows multiple owners of the data. This provides shared ownership.RefCell: Allows mutation of the inner data, even though all theRcpointers are immutable. This combination is essential for data structures like trees or graphs, where multiple nodes might need to hold a mutable reference to the same piece of data (like a shared counter or a neighbor list).
Box
Box is something that stores a pointer.
The Power of Option<Box<T>>: Rust has a feature called Null-Pointer Optimization (NPO). Since Box guarantees its contents are always non-null (it always points to a valid allocation on the heap), Rust can represent the None variant of the Option by simply setting the pointer to null (zero).
Changing type, changes kind of allocation:
Option<Box<T>> is saying: “I might have a pointer to the heap, or I might be null.” (Efficient linked list ending).
Box<Option<T>> is saying: “I definitely have a pointer to the heap, and at that location is a structure that might contain a value.” (Wastes the NPO opportunity).
Readonly classes
Using Superclasses
- No checks that methods in readonly interface are actually side-effect free
- Read-write aliases can occur, e.g., through capturing
- Clients can still use casts to have full access, which is something you may want to prevent at the language design level.
Const Pointers in C++
The constant pointers in C++ implement the readonly type effectively. With constant pointers, you cannot update fields nor mutate anymore.
But again, the client can cast away the const-ness of the object. Another property is that this is not transitive, it must be explicitly described in the code (in the sense that you need to mark which function is ok).