You are on page 1of 59

From “Procedural Style”

to
Basic Objects
CS 1037a – Topic 1
Overview

 from variables to structures


 global functions and call-by-reference
 from structures to objects
• member functions
• information hiding, inheritance
• construction, destruction, and copying of objects
• operator overloading
 detailed review of pointers and
memory management (TOPIC 2)
1–2
What You Should Know
 Variables, built-in types
int num_students = 64;

 Arrays – but just basics


int student_grades[64];
student_grades[7] = 70;

 Procedures, if-else statements, loops


int average_grade() {
int sum = 0;
for (int i = 0; i < num_students; ++i)
sum += student_grades[i];
return sum / num_students;
}
1–3
What You Will Learn
A) Structures and why they’re helpful
struct Point {
int x, y;
}; // now 'Point' is a type, like float

B) Very basic pass-by-reference in C++


// add (dx,dy) to p's coordinate
void shift_by(Point& p, int dx, int dy);

C) Why structures naturally lead to objects


struct Point {
int x, y;
void shift_by(int dx, int dy); // a 'member function'
};
1–4
Procedural Programming
 You already know basics of this style.
procedure definition argument declarations
(aka “function definition”) (aka “formal parameters”)

float vector_length(float x, float y) {


return sqrt(x*x + y*y);
}

float len = vector_length(3.0f,4.0f); // len <-- 5.0

procedure call arguments


(aka “function call”) (aka “actual parameters”)

1–5
Procedural Programming
 Code organised into procedures/functions
 Any function can call any other function
(functions are ‘global’)
global
int student_grades[62];
functions

int average_grade() {...}

void adjust_grades(int amount) {


int i;
for (i=0; i < 62; ++i)
student_grades[i] += amount;
cout << average grade();
}
1–6
Procedural Programming
 Code organised into procedures/functions
 Any function can call any other function
(functions are ‘global’)
 C/C++ defines ‘scope’ of variables...
int student_grades[62];

int average_grade() {...} local


variables
void adjust_grades(int amount) {
int i;
global for (i=0; i < 62; ++i)
variables student_grades[i] += amount;
cout << average grade();
}
1–7
Procedural Programming
 Lots of software written in procedural style
• Linux , Windows kernels (in C)
• Apache web server (in C)
• Javascript , Python interpreters (in C)
• Five gazillion other things (mostly C)

 Sounds good, so why talk of objects?


• Many ‘software engineering’ reasons (blah)
• We only present one practical line of reasoning
• Start by understanding structures
1–8
Part A
Example of a structure:
struct Point {
int x, y;
}; // now 'Point' is a type, like float

Why are structures helpful?

1–9
Compound Variables
 For CPU, these programs are identical...
struct Point {
two compound
int x, y;
variables variable
};

int p1_x, p1_y; Point p1;

void main() { void main() {


p1_x = 5; p1.x = 5;
p1_y = 10; p1.y = 10;
} }

p1_x p1_y p1

... ? ? ? ?5 ? ? ? ?10? ? ? ... ... ? ? ? ?5 ? ? ? ?10? ? ? ...


memory after program runs memory after program runs 1–10
Compound Variables
 Same with these; identical ‘machine code’
struct Point {
int x, y;
};

int p1_x, p1_y; Point p1;


int p2_x, p2_y; Point p2;

void main() { void main() {


p1_x = 5; p1.x = 5;
p1_y = 10; p1.y = 10;

p2_x = p1_x; p2 = p1;


p2_y = p1_y;
} }

1–11
struct Defines New Type
 Not new variable. New variable type.
 After, can declare new vars of that type.
char, Blah p1; // COMPILER ERROR: what is a 'Blah' ??
int,
// Sprechen Sie C++?
float,...

struct Blah {
int x, y; // Oh, so THAT's what a 'Blah' is...
};
char,
int,
float,
Blah p2; // OK, I'll reserve 8 bytes for 'p2'
Blah,...
Blah p3; // OK, and another 8 bytes for 'p3'

 A struct definition defines a new type that is an


aggregate (compound) of existing types
1–12
before Rect...
struct Point {
int x, y;

Compound Variables };

 ‘Existing types’ means earlier structs, too!


struct Rect { struct Rect {
int l, t; Point a;
int r, b; Point b;
}; };

Rect rect; Rect rect;


l r void main() { void main() {
t rect.l = 0; rect.a.x = 0; a
rect.t = 0; rect.a.y = 0;
rect.r = 40; rect.b.x = 40;
b rect.b = 80; rect.b.y = 80; b
} }

Identical machine code; choose whichever you like best.


1–13
Without Compound Variables
 Built-ins (int, float) just building-blocks
• Code gets verbose for many interrelated data, e.g....
void draw_card(int x, int y,
char suit, char denom) {
fill_rect(x, y, x+40, y+80);
draw_char(x, y, denom);
draw_char(x+10, y, suit);
}
(5 of Hearts)
int card_x, card_y;
Wha? char card_suit, card_denom;

draw_card(card_x, card_y,
card_suit, card_denom);
example
output

1–14
With Compound Variables
 Simplifies interface; more expressive code
struct Card { // define a grouping of data
int x, y;
char suit, denom;
};

void draw_card(Card c) {
fill_rect(c.x, c.y, c.x+40, c.y+80);
draw_char(c.x, c.y, c.denom);
draw_char(c.x+10, c.y, c.suit); (5 of Hearts)
}

Card card; // declare a Card variable

draw_card(card);
example
output

1–15
Arrays vs Structures
 “But Prof, can’t I just use an array?”
int rect[4]; // a Rect is just 4 ints
rect[2] = 40; // set right <-- 40
rect[3] = 80; // set bottom <-- 80
draw_rect(rect); // design draw_rect to take array

 Sure you can! Two differences though:


1) Array only accessed by index (rect[2]) ...or even rect[i]
Struct only accessed by member name (rect.right)

2) All array elements must be of same type.


Each struct member can be of different type.
1–16
Arrays vs Structures
 For some cases, array makes sense
struct Point6D {
int x, y, z, t, u, v;
// 6-dimensional (6D) point };
int point[6]; Point6D p;

 For a few cases, tough call (readability?)


int points[50][3]; // 3D pts Point3D points[50];
points[7][1] = 5; points[7].y = 5;

 For many cases, array makes no sense


int card[4]; // yuck :( Card card;
card[3] = (int) 'H'; card.denom = 'H';

1–17
Compound Variables
‘Structure’ suggested by names Structure explicit
struct Card {
int x, y;
char suit, denom;
};
int card_x, card_y;
char card_suit, card_denom; Card card;

 Structs are key to effective procedural


programming.
• Related sets of data explicitly outlined
• Once you ‘get it’, code much less cryptic
• Way easier to pass-by-reference (Part B)

1–18
Important Benefit of Struct
(repeated)

 Without structs, interrelated data means


cumbersome and fragile interfaces
int r_l[3], r_t[3], r_r[3], r_b[3]; Rect r[3];

for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)


draw_rect(r_l[i], r_t[i], draw_rect(r[i]);
r_r[i], r_b[i]);

Hurts brain to read Rect no harder to read


than Point, int, etc
l r
t

b
1–19
Part B
Example of basic pass-by-reference:
// add (dx,dy) to p's coordinates
void shift_by(Point& p, int dx, int dy);

What do references do?


Why do we want them?
1–20
What ‘References’ Do
Pass-by-value arg declaration Reference arg declaration
void negate(int x) { void negate(int& x) {
x = -x; x = -x;
} }

void main() { void main() {


int b = 5; int b = 5;
negate(b); negate(b);
cout << b; cout << b;
} }

negate(b) didn’t affect b negate(b) affected b! 1–21


What ‘References’ Do
Pass-by-value arg declaration Reference arg declaration
void negate(int x) { void negate(int& x) {
x = -x; x = -x;
} }

void main() { void main() {


int b = 5; int b = 5;
negate(b); negate(b);
cout << b; cout << b;
} }

Two int variables will exist: One int variable will exist:
1. b local variable to main 1. b local variable to main
2. x local variable to negate 2. x is really b with a ‘mask on’

b 1–22
Example Motivation
 A function returning one value is easy, e.g.
int minimum(int a, int b) {
if (a < b) return a;
return b;
}

 Problematic example...
int px = 1, py = 4;
(1,4)
// reflect (px,py) along line x=y
int temp = px;
swaps
px , py
px = py; (4,1)
py = temp;

Can “swap two variables” be a function?


1–23
Example Motivation
 Want to call in obvious way, like...
swap(px, py); // modify px and py directly

 But how to define swap function?


void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}

 Wrong: doesn’t modify px, py at all!


• a, b are new int vars just copied from px, py;
a , b get swapped, then promptly forgotten!
1–24
Example Motivation
 Want to call in obvious way, like...
swap(px, py); // modify px and py directly

 Then function needs references to args


void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}

 Now a, b not int vars; just refer to px, py


• While function running, a, b are just ‘aliases’ for
whatever ints passed as actual params (e.g. px, py)
1–25
Point Example
 Problem: want to ‘shift’ existing point by
some dx, dy
Point a;
a.x = a.y = 5; // start with a <-- (5,5) dx

 Could write two += every time (5,5) dy

a.x += 10; // (15,12)


a.y += 7; // a <-- (15,12)

 Or, we can design a function


a = shift_by(a, 10, 7); (1) by-value (copies)
shift_by(a, 10, 7); (2) by-reference
1–26
Point a;
a.x = a.y = 5;
Point Example
(1) Version designed to use copies (by-value)
Point shift_by(Point p, int dx, int dy) {
p.x += dx; // modifies local variable 'p' where p
p.y += dy; // starts life as copy of input argument.
return p; // return copy of modified p
}

a = shift_by(a, 10, 7);

Question: how many times do we copy one


Point variable into another Point variable?
1. value of a is copied into p when function starts
2. value of p is copied into a when function ends
3. Advanced: Point <unnamed> - temporary “return value” variable is copied from p,
so that final value of p can be assigned to a when function ends
1–27
Point a;
a.x = a.y = 5;
Point Example
(2) Version designed to use reference
void shift_by(Point& p, int dx, int dy) {
p.x += dx; // modifies some Point variable 'p' that
p.y += dy; // exists outside this function call
}

shift_by(a, 10, 7); // inside call, 'p' always means 'a'

Question: how many copies this time?


- No copying around is going on

a
1–28
References Are Important for
Procedural Programming
 Can help code readability, function design
 Can be more efficient than copy (e.g. big structs)
struct Card { // Card with its own bitmap
int x, y;
char suit, denom;
unsigned char bitmap[80][40][3]; // 40x80 RGB image
};
// now... do you want draw_card(Card c) or draw_card(Card& c)?

 Other reasons exists outside scope of this


course
1–29
Part C
Example of a basic object:
struct Point {
int x, y;
void shift_by(int dx, int dy); // a 'member function'
};

Why the idea of objects follows


naturally from the idea of structures
1–30
A Pattern in
Procedural Programming
 Composite variable types (struct) tend to
have many naturally associated functions
struct Rect {
int l, t;
int r, b;
};

void set_rect(Rect& rect, int l, int t, int r, int b);


void grow_rect_by(Rect& rect, int dx, int dy);
void shift_rect_by(Rect& rect, int dx, int dy);
bool rect_contains(Rect& rect, Point p);
int rect_area(Rect& rect);
int rect_perimeter(Rect& rect);

1–31
Problem: Repetitive Code
 Repetitive code in function definitions
associated with same struct
bool rect_contains(Rect& rect, Point p) {
return rect.l <= p.x && p.x < rect.r &&
rect.t <= p.y && p.y < rect.b;
}

int rect_area(Rect& rect) {


return (rect.r - rect.l)*(rect.b - rect.t);
}

int rect_perimeter(Rect& rect) {


return 2*(rect.r - rect.l) + 2*(rect.b - rect.t);
}

1–32
Solution: Member Functions
 C++ lets you do exactly same thing but
omit code ‘obvious’ from Rect& context
bool Rect::contains(Point p) {
return l <= p.x && p.x < r &&
t <= p.y && p.y < b;
}

int Rect::area() {
return (r - l)*(b - t); // where will l,t,r,b come from?
} // don't worry, explained soon

int Rect::perimeter() {
return 2*(r - l) + 2*(b - t);
}

1–33
Functions vs Member Functions
Procedural style Object-oriented style
struct Rect { struct Rect {
int l, t; int l, t;
int r, b; int r, b;
};
bool rect_contains(Rect& rect, Point p); bool contains(Point p);
int rect_area(Rect& rect); int area();
int rect_perimeter(Rect& rect); int perimeter();
};
global function declarations member function declarations

bool rect_contains(Rect& rect, Point p) {...} bool Rect::contains(Point p) {...}

int rect_area(Rect& rect) {...} int Rect::area() {...}

int rect_perimeter(Rect& rect) {...} int Rect::perimeter() {...}


global function definitions member function definitions
1–34
Functions vs Member Functions
function definition member function definition
(with code) (with code)
bool rect_contains(Rect& r, Point p) { bool Rect::contains(Point p) {
return r.l <= p.x && p.x < r.r && return l <= p.x && p.x < r &&
r.t <= p.y && p.y < r.b; t <= p.y && p.y < b;
} }
somewhere in main() ... somewhere in main() ...
Rect hitbox; Rect hitbox;
... ...
if (rect_contains(hitbox,princess_pos)) if (hitbox.contains(princess_pos))
fall_in_love(); fall_in_love();

hitbox is conceptually hitbox is conceptually


a compound variable a rectangle object.
of type Rect We can ask it to do
rectangley stuff
1–35
Variables vs Objects
 Underneath, objects are variables (state)
 Distinction is mainly conceptual
• Call it variable to emphasize state/data
update_player_position(mario_state);
update_player_position(princess_state);

• Call it object to emphasize identity/concept


mario.update_position();
princess.update_position();

 Data is more specific than concept, e.g.


struct Rect { struct Rect { struct Rect {
int left, top; int left, top; Point a;
int right, bottom; int width, height; Point b;
}; }; };
1–36
How Member Functions ‘Really’ Work
(A) procedural style (B) object oriented style
struct Point { struct Point {
int x, y; int x, y;
}; void shift_by(int dx, int dy);
};

void Point_shift_by(Point& p, int dx, int dy) { void Point::shift_by(int dx, int dy) {
p.x += dx; x += dx;
p.y += dy; y += dy;
} }

Point p; Point p;

void main() { void main() {


p.x = p.y = 0; p.x = p.y = 0;
Point_shift_by(p, 10, 7); p.shift_by(10, 7);
} }

• Produces identical machine code. CPU doesn’t care.


• Early C++ compilers essentially just translated
code (B)  (A) and relied on C compiler to finish!
1–37
Member Functions Are Not Data
 shift_by() member function does not
add some weird “function thingy” inside
each Point variable... (e.g.)
struct Point { struct Point {
int x, y; int x, y;
}; void shift_by(int dx, int dy);
};

void main() { void main() {


Point p; Point p;
cout << sizeof(p) cout << sizeof(p)
<< " bytes"; << " bytes";
} }

same
size!
1–38
Object Oriented is Soft. Eng.
 Object-oriented (OO) language features
can help, or they can complicate.
• Inheritance struct Circle: public Shape {

• Polymorphism virtual void draw();

private:
• Information hiding float m_radius;
(Encapsulation) };

 Procedural languages (e.g. C) still


facilitate OO design, but code will be
verbose and error-prone.
1–39
This Course = Data Structures
 Not a software engineering course;
we only use basic OO that helps us.
• Inheritance we barely touch this

• Polymorphism we ignore this

• Information hiding we need this (easy)

 Data structures is later topic, but has


nothing to do with struct in C/C++.
confusing terminology alert!
1–40
Motivation for Information Hiding
1. Jane writes Rect library
struct Rect {
int l, t, r, b;
};

2. Jimmy uses it in a million places


int width = hitbox.r - hitbox.l; // calculate width

3. Jane needs to change her data


struct Rect { Million lines of
int l, t, w, h; // width/height stored directly
}; broken code

struct Rect { // Jane


 Jimmy coded for data, not concept ...
int width();
int height();
 Jane should have disallowed private:
int l, t, r, b;
direct access to data in beginning! };
“defensive programming” hitbox.width();1–41
// Jimmy
struct vs class
 C++ kept the struct keyword from C but
added class, mainly for OO terminology
 Difference just conceptual, not technical
• Use struct to suggest “is a variable type” much more
convention
common in
• Use class to suggest “is an object type”
C++
 In C++, both allow data/function members,
inheritance, polymorphism etc. We use m_ prefix
on private member
struct X { class X { variables in this
int public_member; int m_private_member; course (convention)
private: public:
int m_private_member; int public_member;
}; };

Only technical difference is default visibility of members 1–42


Inheritance (Rough Idea)
 Complex subject, many interpretations
• subclass vs. subtype vs. is-a
 Is-a interpretation works often enough:
• If “X is-a Y” for all X, then X inherits from Y
“Circle is-a Shape” In C++, maybe data like ...
class Shape {
“Square is-a Shape”...
public:
Point origin;
superclass };
Shape
(aka base class)
class Circle: public Shape {
float radius;
};
Circle Square Line
class Square: public Shape {
subclasses float side_length;
};
(aka derived classes) 1–43
Constructors, Destructors, and
Copy Operators in C++

overview

1–44
struct Card {
int x, y;
char suit, denom;
};

Need For Constructors


 Variables (incl. objects) start with random data
card
void main() {
Card card; // uninitialised! ... ? ? ? ? ? ? ? ? ? ? ? ...
draw_card(card);
} x y denom
suit

 Uninitialised variables huge source of bugs


• Most objects need sensible data before use
• Some languages force initialisation first (e.g. C#)
 Constructors are OO feature for consistent,
automatic initialisation of objects in C++
1–45
Constructor
 Just a member function named after class
class Point { Omit the void
public: void Point();
int x, y;
Point(); // declare default constructor
Point(int ax, int ay); // declare an extra constructor
}; // (can add more constructors if convenient)

Point::Point() { x = y = 0; } // make (0,0) the default


Point::Point(int ax, int ay) { x = ax; y = ay; }

 Called automatically on object declaration


void main() {
Point p1; // 1st constructor called, so p1 = (0,0)
Point p2(4, 1); // 2nd constructor called, so p2 = (4,1)
}
Special C++ notation for passing arguments to a constructor 1–46
Function vs Default Constructor
Initialisation function (manual) Constructor (automatic!)
struct Card { class Card {
public: public:
int x, y; Card(); // declaration
char suit, denom; int x, y;
}; same char suit, denom;
};
// set default state
as
void card_init(Card& c) { Card::Card() { // definition
c.x = c.y = 0; x = y = 0; //
c.denom = '2'; // two of denom = '2'; //
c.suit = 'H'; // hearts suit = 'H'; //
} } //

void main() { void main() {


Card c; // uninitialised Card c; // initialised!
card_init(c); draw_card(c);
draw_card(c); }
}

Manual initialisation easily forgotten: a classic source of bugs! 1–47


Copying Objects
 Variables often copied in practice
(e.g. in swap(a,b) we need a temp copy)
 By default, objects are copied “raw”
(that is, values of all member variables are copied)
Point p1, p2(4,1);
Point p3 = p2; // (1) initialise p3 - copy 8 bytes from p2
p1 = p3; // (2) assign value to p1 - copy 8 bytes from p3

 C++ lets you code your own copy operations.


Just add two special function members:
class Point {
Point(const Point& src); // "copy constructor"
void operator=(const Point& src); // "copy (a.k.a. assignment) operator"
};
Don’t worry if you don’t know what “operator blah” or “const” mean for now.
Just imitate, and make sure your class name appears in right spots. 1–48
Copying Objects
Point p1, p2(4,1);
Point p3 = p2; // (1) initialise using copy constructor
Point p4(p1); // (1) alternative way to call copy constructor
p1 = p3; // (2) assign using copy (a.k.a. assignment) operator

 Copy constructor called if newly declared


object initialised from another object (1)
 Copy operator (a.k.a. assignment operator)
called when existing object copies its value
(data) from another object (2)
E.g. definitions below make (1) and (2) do “raw” copying of all member variables that
would not differ from default copying, but you can put any other code for custom “copying”.
Point::Point(const Point& src) { x=src.x; y=src.y; }
void Point::operator=(const Point& src) { x=src.x; y=src.y; }

Point doesn’t really need custom “copy”. But, later we will use classes 1–49
where “raw” copying of values for member variable is not good enough!!!
More Advanced Constructors
 Member variables must also be constructed
class Rect {
public:
Point a, b; // Point class has its own constructor!
Rect();
Rect(Point tl, Point br);
};

Rect::Rect() { } // default constructors for a & b called implicitly


Rect::Rect(Point tl, Point br): a(tl), b(br) { }

 Examples syntax: constructor can explicitly call any member variable


constructors (e.g. copy constructors) before {...} as above
void main() { void main() {
Point p1, p2(40,80); Rect empty;
Rect r(p1,p2); Rect r(Point(),Point(40,80));
} }
C++ notation for creating temporary, unnamed objects of type Point 1–50
Destructors
 Constructors handle ‘birth’ of an object
 Destructors handle deaaath
void main() {
Point p1; // p1 is born!!
...
} // p1 went out of {scope} ... gone, forgotten, DEAD

 Simple variables don’t have anything to


“clean up” before they die, but complex
objects definitely do! (e.g. data strutures)
class Point { // silly destructor definition
... Point::~Point() {
~Point(); // destructor cout << "A Point died! ";
}; }
1–51
Operator Overloading in C++

overview

1–52
Some standard operators
 Some standard operators are defined for
variables of standard types like int, float, char,…
- operator+, operator-, operator+=, operator*, operator>, operator<= …
- operator= (copy or assignment operator)

int a=2, b=5, c=a+b; a+=c; bool h = (c<=b);

Q: Can expression c=a+b; work for objects Point a,b,c; ?

Q: What should expression c=a+b; mean for Points?


… for objects/variables of other classes?
1-53
Some standard operators
 Some standard operators are defined for
variables of standard types like int, float, char,…
- operator+, operator-, operator+=, operator*, operator>, operator<= …
- operator= (copy or assignment operator)

int a=2, b=5, c=a+b; a+=c; bool h = (c<=b);

 We already saw that operator= (copy operator)


can be defined for objects of ANY class, e.g…
void Point::operator=(const Point& src) { x=src.x; y=src.y; }

allows to have code like Point a, b; … b=a;

1-54
Overloading Operators
C++ allows to define (overload)
any operator for any class of objects!
Operator overloading can simplify code
making it read like basic math. For example:
operator+ can be defined for - two Points (vector addition)
- Rect and Point (e.g. shifting Rect by a vector)

operator* can be defined for - two Points (scalar or dot product)

Your code should define the meaning


of operations over non-standard types 1-55
Assume: Point a(2,1),b(2,7);
Point c;

Example 1: adding points (vectors)


Expressions like c = a+b; for three Points a, b, c
can be used if there exists operator

Point operator+ (const Point& a, const Point& b)


{
Point tmp( a.x+b.x, a.y+b.y );
return tmp;
}

Such operator+ is not standard (for Points) and it has to


be defined somewhere in your code. Otherwise c=a+b;
will NOT compile and you would have to use code like
c.x=a.x+b.x; c.y=a.y+b.y; or c = Point(a.x+b.x, a.y+b.y);
1-56
Assume: Point b, a(2,1);
double s = 4;

Example 2: multiplication for points


Expressions like b = s*a; for two Points a, b, and scalar s
can be used if there exists operator

Point operator* (const int& s, const Point& a)


{
Point tmp ( s*a.x, s*a.y );
return tmp;
}

Such operator* is not standard. If it is not defined in you


code, you can not write b = s*a; - this will not compile.
Instead, you’d have to write b.x=s*a.x; b.y=s*a.y;
1-57
Assume: Point b(3,2), a(2,1);
double s;

Example 3: “dot” product for points


Expressions like s = a*b; for two Points a, b, and scalar s
can be used if there exists operator

double operator*(const Point& a, const Point& b)


{
return a.x*b.x+a.y*b.y;
}

operator* above allows to replace an expression like


s = a.x*b.x+a.y*b.y; by a simpler one s = a*b;

1-58
Overloading Operators
some operators could be class members,
while others could be global operators
declarations inside file Point.h
class Point {
int x, y; copy operator
...
void operator=(const Point& src); // some operators must be declared
void operator+=(const Point& dP); // as members of class Point
};
// while others (e.g. operator+) could be
Point operator+ (const Point& a, const Point& b); // declared globally

definitions (implementations) inside file Point.cpp


void Point::operator=(const Point& src) { x=src.x; y=src.y; }
void Point::operator+=(const Point& dp) { x+=dp.x; y+=dp.y; }

Point operator+ (const Point& a, const Point& b) { return Point(a.x+b.x,a.y+b.y);}


1-59

You might also like