- Built-in primitive types (
int, double, char) are limiting: not everything is a number or character - Real-world entities are made up of multiple aspects (data)
- Example: Lab 10: sorting teams
- Kept a lot of different, but related data in different
- Every swap required a swap of every array
- Very easy to screw up the book keeping
- Very inconvenient to treat related pieces of data as unrelated
- Encapsulation:
- The grouping of data
- The protection of data
- The grouping of functionality that acts on that data
- C only provides #1: "weak encapsulation", the grouping of data
- C provides weak encapsulation through structures
- Syntax:
typedef struct {
int day;
int month;
int year;
} Date;
typedef struct {
int nuid;
char *firstName;
char *lastName;
double gpa;
Date dateOfBirth;
} Student;- In general, the order of the individual "member variables" does not matter
- However, the order that you declare structures does matter: you cannot use a structure within another structure unless it is declared first
- When a structure "owns" an instance of another structure, it is called composition
- Typically, structures are declared in header files,
student.h(example) - Naming conventions:
UpperCamelCasingfor structure nameslowerCamelCasingfor "member" variables
- each "field" or "member variable" or "component" is separated by a semicolon (one to a line)
- Once declared, you can create a structure like any other variable
Student me;
me.nuid = 35140602;
me.lastName = (char *) malloc(sizeof(char) * 7);
strcpy(me.lastName, "Bourke");
me.firstName = (char *) malloc(sizeof(char) * 6);
strcpy(me.firstName, "Chris");
me.gpa = 4.0;
me.dateOfBirth.day = 9;
me.dateOfBirth.month = 7;
me.dateOfBirth.year = 1990;- Use the dot operator to access member fields
- Creating complex structures is tedious and error prone, so it is generally good to write "constructor" or "factory" functions
Student * createStudent(int nuid,
char * firstName,
char * lastName,
double gpa,
Date dateOfBirth) {
Student *s = (Student *) malloc(1 * sizeof(Student));
s->nuid = nuid;
s->gpa = gpa;
s->dateOfBirth = dateOfBirth;
s->firstName = (char*)malloc(sizeof(char) * (strlen(firstName)+1));
strcpy(s->firstName, firstName);
s->lastName = (char*)malloc(sizeof(char) * (strlen(lastName)+1));
strcpy(s->lastName, lastName);
//this would only be a shallow copy:
//s->firstName = firstName;
return s;
}
//...
Date dateOfBirth = {7, 9, 1990};
Student *me = createStudent(35140602, "Chris", "Bourke",
4.0, {7, 9, 1990});- Demonstration using Eclipse
- An object is an entity with identity, state, and behavior
- Identity: a way to distinguish one object from another (either
==or defining anequals()method) - State: an object's member variables whose scope is limited to an instance of an object
- Behavior: methods that act on an object's state belong in the object
- Identity: a way to distinguish one object from another (either
- Java is an OOP language that uses classes to represent objects
- Naming conventions
- Name of a class uses
UpperCamelCasing - Member variables and member methods use
lowerCamelCasing(variables are nouns, methods are verbs) - Member variables are declared by not using the
statickeyword (staticmeans that a variable or method belong to the class and not to instances of the class)
- Name of a class uses
- To create new instances of a class you "instantiate" a new object using the
newkeyword which invokes a constructor method- By default all objects have a default no-argument constructor
- You can define any number of custom constructors: a constructor that takes all member variables, a constructor that only takes a few, or a copy constructor
- By default, any member variable that is not set has a default value of 0,
falseornulldepending on its type - Its best practice to always reuse existing objects (
String,LocalDate, etc.): this is composition - Don't over-engineer your objects:
- First name/last name: don't create a
Nameclass - An
Addressmay have a street, city, state, zip, but each one of these may not be decomposable further (ie don't define aState,Cityobject, etc. UNLESS you really need to)
- First name/last name: don't create a
- YAGNI: You Ain't Gonna Need It
-
You can achieve the #2 of encapsulation (protection of data) using visibility keywords
private: only the class and its subclasses (as well as other instances) can "see" the variablesprotected: only the class and its subclasses can see the variable- The absence of any keyword makes the variable "package protected": any class in the same package can see the variable
public: ANY piece of code can see the variable
-
In general you should prefer the most conservative visibility (
private) unless you have a Very Good Reason to do otherwise -
You should prefer to make all member variables
privateand control access through constructors and/or getters/setters -
The
thiskeyword allows you to access an instance's variables and methods ("Open Recursion")
- Often you need to create entire collections (arrays) of structures
int n = 10;
//create an array of 10 integers:
int *arr = (int *) malloc(n * sizeof(int));
arr[0] = 10;
//create an array of 10 Students:
Student *roster = (Student *) malloc(n * sizeof(Student));
//memory leak: roster[0] = *createStudent(...);
Student *s = createStudent(...);
roster[0] = *s;
free(s);- Arrays suck, use dynamic data structures like Lists, Sets, Maps, etc.
Student s1 = new Student(35140602, "Chris", "Bourke", 4.0, dateOfBirth);
Student s2 = new Student(1234, "Scott", "Frost", 2.6, dateOfBirth);
List<Student> roster = new ArrayList<Student>();
roster.add(s1);
roster.add(s2);
roster.add(new Student(...));
roster.add(s1); //lists allow duplicates!
Set<Student> roster2 = new HashSet<Student>();
roster.add(s1);
roster.add(s2);
roster.add(s1); //duplicate, it will have no effect
//**** it will have no effect assuming that we've defined
//the equals() and hashCode() methods
Map<Integer, Student> nuidMap = new HashMap<Integer, Student>();
nuidMap.put(s1.getNuid(), s1);
nuidMap.put(s2.getNuid(), s2);- Alternatively in C, you can design an "initializer" function
- A factory function creates a new (dynamically allocated) structure
void initStudent(Student *s, int nuid, const char * firstName,
const char * lastName, double gpa) {
s->nuid = nuid;
s->gpa = gpa;
//TODO: make a deep copy instead!
s->firstName = firstName;
//TODO: make a deep copy instead!
s->lastName = lastName;
return;
}- Create a function that creates a string representation of a
Studentstructure.
char * studentToString(const Student *s) {
char temp[1000];
sprintf(temp, "%s, %s (%08d) %.2f", s->lastName, s->firstName, s->nuid, s->gpa);
char *str = (char *) malloc( (strlen(temp) + 1) * sizeof(char) );
strcpy(str, temp);
return str;
}