What is an abstract data type in object oriented programming?

What is an abstract data type in object oriented programming? I've gone through the wiki for this topic, but I am still unclear about it. Could someone clarify?


An abstract class is a generalization concept. It is a class you invent to only use as a base class for inheritance but not to instantiate objects from.

And abstract datatype (ADT) is not necessarily an OOP concept. It is an older term to describe the concepts of for example Stack and Queue in terms of their functionality, without describing the implementation.


There is a difference between an "abstract data type" and an "abstract class".

An abstract class is one that may not have definitions for all the methods it defines. You therefore cannot directly instantiate an abstract class. You have to create a subclass and then instantiate that.

An abstract data type is a model of a certain kind of data structure e.g. a Stack. A Stack has push() and pop() operations and that have well-defined behaviour.

The abstract data type (ADT) itself refers to this model, not any particular implementation in any particular programming language or paradigm. You could implement a Stack in an object-oriented language, but you could also implement it in a functional programming language.

ADTs allow discussion about the properties of Stacks, Queues etc that hold for all correct implementations of the ADT.


Well, it's all about abstraction. Abstraction is particularly useful in programming. The main advantage is ability to hide realization details. You hide it inside one modules (so-called "server modules") and provide some public interface for other modules (so-called "client modules"). And now we have three different possibilities:

Server module can supply an abstract data structure (ADS) itself.

In that case it contains ADS entity itself. The public interface consists of some procedures (and maybe some constants).

Interface of server module (stack_ads.h):

#ifndef STACK_ADS
#define STACK_ADS

const int capacity = 10;

void clear();
int size();
int pop();
void push(int value);

#endif STACK_ADS

Implementation (stack_ads.cpp):

#include "stack_ads.h"

int items[capacity];
int top = -1;

void clear()
{
  top = -1;
}

int size()
{
  return top + 1;
}

int pop()
{
  top -= 1;
  return items[top + 1];
}

void push(int value)
{
  top += 1;
  items[top] = value;
}

In the client module (main.cpp) we import server module and use data structure directly.

#include <iostream>
#include "stack_ads.h"

int main (int argc, char* const argv[]) 
{
  push(1);
  push(2);
  push(3);

  std::cout << pop() << std::endl;
  std::cout << pop() << std::endl;
  std::cout << pop() << std::endl;

  return 0;
}

Server module can supply an abstract data type (ADT) in the form of struct/record.

In client module we can declare variables to be of that type. Because a module is free to declare more than one variable to be of the exported type, it can have more than one data structure. Each abstract data structure is variable of abstract data type.

Interface (stack_adt.h):

#ifndef STACK_ADT
#define STACK_ADT

const int capacity = 10;

typedef struct
{
  int items[capacity];
  int top;
} StackADT;

void clear(StackADT* stack);
int size(StackADT* stack);
int pop(StackADT* stack);
void push(StackADT* stack, int value);  

#endif STACK_ADT

Implementation (stack_adt.cpp):

#include "stack_adt.h"

void clear(StackADT* stack)
{
  stack->top = -1;
}

int size(StackADT* stack)
{
  return stack->top + 1;
}

int pop(StackADT* stack)
{
  stack->top -= 1;
  return stack->items[stack->top + 1];
}

void push(StackADT* stack, int value)
{
  stack->top += 1;
  stack->items[stack->top] = value;
}

Client module:

#include <iostream>
#include "stack_adt.h"

int main (int argc, char* const argv[]) 
{
  StackADT stack1;
  StackADT stack2;
  stack1.top = -1;
  stack2.top = -1;

  push(&stack1, 1);
  push(&stack1, 2);
  push(&stack1, 3);

  std::cout << pop(&stack1) << std::endl;
  std::cout << pop(&stack1) << std::endl;
  std::cout << pop(&stack1) << std::endl;

  push(&stack2, 10);
  push(&stack2, 20);
  push(&stack2, 30);

  std::cout << pop(&stack2) << std::endl;
  std::cout << pop(&stack2) << std::endl;
  std::cout << pop(&stack2) << std::endl;

  return 0;
}

Finally the server module can supply an abstract data type (ADT) in the form of class.

If our language support OOP we can describe ADT by means of classes. And once again in client module we can declare variables to be of that type. In object-oriented terminology, the type is called a class, and the variable with that type is called an object.

Server module interface (Stack.h):

#ifndef STACK
#define STACK

const int capacity = 10;

class Stack
{
public:
  Stack();
  void clear();
  int size();
  int pop();
  void push(int value);
private:
  int items[capacity];
  int top;
};

#endif STACK

Implementation (Stack.cpp):

#include "Stack.h"

Stack::Stack()
{
  this->top = -1;
}

void Stack::clear()
{
  this->top = -1;
}

int Stack::size()
{
  return this->top + 1;
}

int Stack::pop()
{
  this->top -= 1;
  return this->items[this->top + 1];
}

void Stack::push(int value)
{
  this->top += 1;
  this->items[this->top] = value;
}

The differences between two last options are:

  • Terminological mentioned above (type <-> class, variable <-> object).
  • In the non-class ADT, the formal parameter list of every procedure must include a variable s of type Stack. In the stack class, the specification of the data structure s is not included with the other formal parameters following the name of the procedure, but stands alone enclosed in parentheses before the name of the procedure. Using Smalltalk terminology formal parameter before the procedure name is called the receiver.
  • The location of the procedures. In the non-class ADT, the procedures are located outside the Stack struct. In the class, the procedures are located within the class. In object-oriented terminology, procedures that have receivers, and are therefore contained within a class type, are called methods.

Client code:

#include <iostream>
#include "stack.h"

int main (int argc, char* const argv[]) 
{
  Stack stack1;
  Stack stack2;

  stack1.push(1);
  stack1.push(2);
  stack1.push(3);

  std::cout << stack1.pop() << std::endl;
  std::cout << stack1.pop() << std::endl;
  std::cout << stack1.pop() << std::endl;

  stack2.push(10);
  stack2.push(20);
  stack2.push(30);

  std::cout << stack2.pop() << std::endl;
  std::cout << stack2.pop() << std::endl;
  std::cout << stack2.pop() << std::endl;

  return 0;
}