Spring 2015: C Programming for chemists
(with elements of C++)
Davide Ceresoli ([email protected])
Instituto di Scienze e Tecnologie Molecolari (CNR-ISTM)
Outline
1
User defined types (
struct
)
2
Object Oriented Programming (
class
)
3
Generic programming (
template
)
User defined types
Scalars and C++ containers
Until now we have seen
scalar
and
vector
data types. In addition to the
vector<>
, C++ provides you with
lists
,
queues
,
sets
and
dictionaries (aka
maps)
.
The main feature of these
standard containers
is that every element stored
in the container
must have the same data type
.
User-defined types
Structs
C(++)
struct
’s
A C(++)
struct
is a compound of related variables, possible of different
types.
Examples:
t y p e d e f s t r u c t {
s t r i n g n a m e ; u n s i g n e d int i d _ m a t r i c o l a ; vector <int> m a r k s ; } S t u d e n t ;
v o i d p a s s ( S t u d e n t & s t u d e n t ) {
d o u b l e ave = 0;
for (int i = 0; i < s t u d e n t . m a r k s . s i z e (); i ++) ave += i ;
ave = ave / d o u b l e( s t u d e n t . m a r k s . s i z e ( ) ) ;
Structs
Structure types are declared with
typedef struct
{...}
Type;
It is a good programming practice to collect variables which are
logically and functionally
related
into the same struct.
It is more efficient to
pass-by-reference
(i.e. using the &) in order to
avoid
copies
.
You can access the
member values
using the dot (
var.member
).
The use to structured types leads you to more
generic code
and
modularization
.
The combination of structures and member functions is a
class
C++ periodic table
One possibility is:
c o n s t u n s i g n e d int n _ e l e m e n t s = 1 1 6 ; // w h a t is n e w e l e m e n t s a r e d i s c o v e r e d ? vector < string > e l e m e n t _ n a m e ( n _ e l e m e n t s );
vector <double> e l e m e n t _ m a s s ( n _ e l e m e n t s );
vector <double> e l e m e n t _ m e l t i n g _ t e m p e r a t u r e ( n _ e l e m e n t s ); vector <double> e l e m e n t _ b o i l i n g _ t e m p e r a t u r e ( n _ e l e m e n t s ); vector <double> e l e m e n t _ i o n i c _ r a d i u s ( n _ e l e m e n t s ); ...
C++ periodic table
How about this:
t y p e d e f s t r u c t {
int i s o t o p e ; // i . e . 2 3 9 f o r U r a n i u m
int protons , n e u t r o n s ; d o u b l e mass , a b u n d a n c e ; d o u b l e s p i n ;
... } I s o t o p e ;
t y p e d e f s t r u c t {
int a t o m i c _ n u m b e r ; s t r i n g n a m e ;
s t r i n g s y m b o l ;
d o u b l e mass , m e l t i n g _ t e m p e r a t u r e , b o i l i n g _ t e m p e r a t u r e ; d o u b l e i o n i c _ r a d i u s ;
...
vector < Isotope > i s o t o p e s ; vector <int> o x i d a t i o n _ s t a t e s ; } E l e m e n t ;
t y p e d e f vector < Element > P e r i o d i c T a b l e ; // c a n a c c o m m o d a t e n e w e l e m e n t s // or :
C++ periodic table
Extracting information
We have clearly improved to readability and logic of the code, at the price
of writing specialized functions:
v o i d f i n d _ i s o t o p e ( P e r i o d i c T a b l e & pt , s t r i n g symbol , int isotope , I s o t o p e & iso ) {
for (int i = 0; i < pt . s i z e (); i ++) {
if ( pt [ i ]. s y m b o l == s y m b o l ) {
E l e m e n t & el = pt [ i ];
for (int j = 0; j < el . i s o t o p e s . s i z e (); j ++) {
if ( el . i s o t o p e s [ j ]. i s o t o p e == i s o t o p e ) {
iso = el . i s o t o p e s [ j ]; r e t u r n;
} }
c o u t < < " i s o t o p e " < < s y m b o l < < i s o t o p e < < " d o e s not e x i s t s " < < e n d l ; }
c o u t < < " e l e m e n t " < < s y m b o l < < " d o e s not e x i s t s " < < e n d l ; }
C++ periodic table
A more elegant way
The code in the previous page assumes that PeriodicTable is a
vector
that can be indexed by
[..]
.
v o i d f i n d _ i s o t o p e ( P e r i o d i c T a b l e & pt , s t r i n g symbol , int isotope , I s o t o p e & iso ) {
for ( pt :: i t e r a t o r el = pt . s t a r t (); el != pt . end (); el ++) {
if ( el - > s y m b o l == s y m b o l ) {
vector < I s o t o p e s >& i s o t o p e s = el - > i s o t o p e s ; // g e t an alias , n o t a c o p y
for (int j = 0; j < i s o t o p e s . s i z e (); j ++) {
if ( i s o t o p e s [ j ]. i s o t o p e == i s o t o p e ) {
iso = i s o t o p e s [ j ]; r e t u r n;
} }
c o u t < < " i s o t o p e " < < s y m b o l < < i s o t o p e < < " d o e s not e x i s t s " < < e n d l ; }
c o u t < < " e l e m e n t " < < s y m b o l < < " d o e s not e x i s t s " < < e n d l ; }
Classes and objects
A PeriodicTable C++
class
If we have a number of functions that work on the PeriodicTable type, we
can put them
inside
the type, and make a C++
class
.
Example:
t y p e d e f s t r u c t {
int i s o t o p e ; // s a m e as b e f o r e ...
} I s o t o p e ;
t y p e d e f s t r u c t {
int a t o m i c _ n u m b e r ; // s a m e as b e f o r e ...
} E l e m e n t ;
c l a s s P e r i o d i c T a b l e {
p r i v a t e:
list < Element > m _ t a b l e ;
p u b l i c:
Classes and objects
continued:
...
v o i d a d d E l e m e n t ( E l e m e n t & el );
v o i d l o a d T a b l e F r o m F i l e ( i f s t r e a m & fin ); v o i d s a v e T a b l e T o F i l e ( o f s t r e a m & f o u t );
E l e m e n t & g e t E l e m e n t B y N a m e ( s t r i n g n a m e ); E l e m e n t & g e t E l e m e n t B y S y m b o l ( s t r i n g s y m b o l ); E l e m e n t & g e t E l e m e n t B y N u m b e r (int a t o m i c _ n u m b e r ); ...
};
int m a i n () {
P e r i o d i c T a b l e t a b l e (); // i n v o k e t h e c o n s t r u c t o r i f s t r e a m fin (" p e r i o d i c _ t a b l e . dat "); // a l s o t h i s is a c o n s t r u c t o r t a b l e . l o a d T a b l e F r o m F i l e ( fin );
E l e m e n t & s i l i c o n = t a b l e . g e t _ e l e m e n t _ b y _ s y m b o l (" Si ");
c o u t < < " Si has " < < s i l i c o n . i s o t o p e s . s i z e () < < " i s o t o p e s " < < e n d l ; ...
Classes and objects
Let’s go back
Classes and objects
Classes that behave like numbers
In C++ it is possible to re-define (aka
overload
the behavior of the
arithmetic
+,-,*,/,..
and mathematical functions in order to make
classes that behave like numbers (i.e. complex numbers and rational
numbers)!
A C++ complex number is already present in the C++ standard library:
# i n c l u d e < i o s t r e a m > # i n c l u d e < cmath > # i n c l u d e < complex > u s i n g n a m e s p a c e std ;
int m a i n () {
complex <double> a = 1 . 0 + 3 . 0 I ; // ( 1 + 3 i ) complex <double> b = s q r t ( a );
c o u t < < b < < e n d l ; ...
Classes and objects
c l a s s r a t i o n a l {
p r i v a t e: int num , den ; v o i d s i m p l i f y ();
v o i d r a t a p p r o x (d o u b l e x , int p r e c );
p u b l i c:
// c o n s t r u c t o r s
r a t i o n a l (int num , int den ): num ( num ) , den ( den ) { s i m p l i f y (); } r a t i o n a l (int n ): num ( num ) , den (1) {}
r a t i o n a l (d o u b l e x , int p r e c i s i o n = 1 0 ) { r a t a p p r o x ( x , p r e c i s i o n ); } ...
};
r a t i o n a l & o p e r a t o r+(c o n s t r a t i o n a l & a , c o n s t r a t i o n a l & b ) {
r e t u r n r ( a . num * b . den + b . num * a . den , a . den * b . den ); }
...
int m a i n () {
r a t i o n a l a (1 ,3) , b (4 ,5);
r a t i o n a l c = a + b ; // 1 / 3 + 4 / 5 = ? ...
Classes and objects
Discussion
Object-Oriented Programming (OOP) offers you the possibility to
program:
1
Encapsulation:
packing of data and functions into a single component.
2Information hiding:
hide and separate the implementation from the
interface
3
Data abstraction:
making objects that behave like numbers, arrays,
other objects
Objects can
inherit
functionality from other objects
Enforce the logic, modularization and code reuse by creating simple
objects
(Cons) More complicated w.r.t. imperative programming, more code
Generic programming
Function overloading
In C++ (not in C!) you can define functions with the same name, but
different argument types (
overloading
). The compiler will pick the one
that matches the actual number and type of arguments.
The best example is
random number
:
int r a n d o m _ n u m b e r (int b ); // [ 1 ]
int r a n d o m _ n u m b e r (int a , int b ); // [ 2 ]
d o u b l e r a n d o m _ n u m b e r (); // [ 3 ]
d o u b l e r a n d o m _ n u m b e r (d o u b l e b ); // [ 4 ]
d o u b l e r a n d o m _ n u m b e r (d o u b l e a , d o u b l e b ); // [ 5 ] ...
int m a i n () {
c o u t < < r a n d o m _ n u m b e r ( 1 0 ) < < e n d l ; // c a l l s [ 1 ] c o u t < < r a n d o m _ n u m b e r ( 1 0 . 0 ) < < e n d l ; // c a l l s [ 4 ] c o u t < < r a n d o m _ n u m b e r (0.0 , 3 . 0 ) < < e n d l ; // c a l l s [ 5 ]
c o u t < < r a n d o m _ n u m b e r (0 , 3 . 0 ) < < e n d l ; // e r r o r : no m a t c h i n g f u n c t i o n s t r i n g s ;
c o u t < < r a n d o m _ n u m b e r ( s ) < < e n d l ; // e r r o r : no m a t c h i n g f u n c t i o n ...
Generic programming
Template functions
In C++ (not in C!) you can define functions
templates
with generic types.
Each type the function is invoked with a concrete type, the compiler will
generate a new function automatically.
The best example is the
max(a,b)
function:
t e m p l a t e<t y p e n a m e T > // T is a g e n e r i c t y p e T & max ( T & a , T & b )
{
if ( a > b ) r e t u r n a ; r e t u r n b ;
} ...
int m a i n () {
c o u t < < max (1 ,2) < < e n d l ; // g e n e r a t e s : i n t & m a x ( i n t & a , i n t & b )
c o u t < < max (" p i p p o "," p l u t o ") < < e n d l ; // g e n e r a t e s : s t r i n g & m a x ( s t r i n g & a , s t r i n g & b ) c o u t < < max (1 ," p l u t o ") < < e n d l ; // e r r o r : no m a t c h i n g t e m p l a t e
complex <double> a , b ;
c o u t < < max ( a , b ) < < e n d l ; // e r r o r : no ’ < ’ o p e r a t o r w i t h c o m p l e x n u m b e r s ...
OOP and templates
Practical
1
Let’s write a generic sort function acting on a