class Rect{
public:
int width,height;
int getArea(){
return width*height;
}
};
int main(){
Rect myrect;
myrect.width=2;
myrect.height=3;
cout<<"myrect.area() is "<<myrect.getArea();
return 0;
}
myrect.area() is 6
Constructor คือฟังก์ชันที่ถูกเรียกขณะทำการสร้าง instance (หรือ object) โดยชื่อฟังก์ชันจะต้องชื่อเดียวกับคลาสและไม่มีการ return
class Rect{
public:
int width,height;
Rect(int w, int h){
width=w; height=h;
}
int getArea(){
return width*height;
}
};
int main(){
Rect myrect(2,3);
cout<<"myrect.area() is "<<myrect.getArea();
return 0;
}
myrect.area() is 6
Deconstructor คือฟังก์ชันขณะที่ทำลายคลาสและคืนหน่วยความจำ(deallocation)
class Rect{
public:
int width,height;
Rect(int w, int h){ width=w; height=h; }
~Rect(){
cout<<"Removing\n";
}
int getArea(){ return width*height; }
};
int main(){
Rect myrect(2,3);
cout<<"myrect.area() is "<<myrect.getArea()<<endl;
return 0;
}
myrect.area() is 6
Removing
Accessors (หรือบ้างครั้งเรียกว่า Getter) ใช้เพื่ออ่านค่า Attributes จากภายนอกคลาส
Modifiers (หรือบ้างครั้งเรียกว่า Setter หรือ Mutator) ใช้เพื่อกำหนดค่าของ Attributes จากภายนอกคลาส
class Obj{
public:
float getMass(){
return mass;
}
void setMass(float m){
mass=m;
}
private:
float mass;
};
int main(){
Obj stone;
stone.setMass(5);
cout<<"Mass is "<<stone.getMass()<<endl; //cannot access getArea()
return 0;
}
Mass is 5
static เขียนหน้าตัวแปรเมื่อต้องการ allocation เพียงครั้งเดียว ซึ่งจะให้ผลเฉพาะเมื่อใช้ใน function และ class
เมื่อใช้เขียนหน้าตัวแปรใน function ตัวแปรนั้นจะจำค่าเดิมไว้สำหรับการเรียกใช้ครั้งหน้า
#include<iostream>
using namespace std;
int myFunc();
int main(){
cout<<myFunc()<<endl;
cout<<myFunc()<<endl;
cout<<myFunc()<<endl;
cout<<myFunc()<<endl;
}
int myFunc(){
static int x=0;
return x++;
}
0
1
2
3
เมื่อใช้เขียนหน้าตัวแปรในคลาส ตัวแปรนั้นจะแชร์ให้ทุก instances ที่เป็นคลาสประเภทเดียวกันเข้าถึงได้ สังเกตการกำหนดค่าเริ่มต้น int Obj::x=5; อยู่นอกคลาส
#include<iostream>
using namespace std;
class Obj{
public:
static int x;
void setX(int _x){
x=_x;
}
int getX();
};
int Obj::x=5;
int main(){
Obj myobj1;
Obj myobj2;
myobj1.setX(7);
cout<<"myobj1.x: "<<myobj1.x<<endl;
cout<<"myobj2.x: "<<myobj2.x<<endl;
myobj1.setX(13);
cout<<"myobj1.x: "<<myobj1.x<<endl;
cout<<"myobj2.x: "<<myobj2.x<<endl;
return 0;
}
int Obj::getX(){
return x;
}
Running /home/ubuntu/workspace/code32_static_class.cpp
myobj1.x: 7
myobj2.x: 7
myobj1.x: 13
myobj2.x: 13
const หลังฟังก์ชันเพื่อไม่ให้แก้ไข data member ในคลาส
#include<iostream>
using namespace std;
class Obj{
public:
int x;
mutable int y;
//const int z=4; wont compile
void setX(int _x) {
x=_x;
}
void setY(int _y) const{
y=_y;
const int beta=8;
}
};
int main(){
Obj obj1;
obj1.setX(5);
obj1.setY(7);
const int alpha=4;
cout<<"x,y :"<<obj1.x<<","<<obj1.y<<endl;
cout<<"alpha: "<<alpha<<endl;
return 0;
}
Running /home/ubuntu/workspace/code33_mutable.cpp
x,y :5,7
alpha: 4
เราสามารถสร้าง instance จาก class และคัดลอก instance ดังกล่าวเพื่อส่งให้ function ประมวลผลต่อได้ ตัวอย่างโปรแกรม
#include<iostream>
using namespace std;
class Obj{
public:
int x;
};
void showX_from_outside(Obj a){
cout<<a.x;
}
int main(){
Obj a;
a.x=7;
showX_from_outside(a);
}
Running /home/ubuntu/workspace/code411_pass_obj_to_func.cpp
7
friend ใส่นำหน้าฟังก์ชัน เมื่อต้องการให้ฟังก์ชันนั้นเข้าถึง private data members หรือ private member functions ภายในคลาส
#include<iostream>
#include <string>
using namespace std;
//no function prototype is required
class Man{
private:
string hand;
void isTouch (){ hand+="is touched"; }
friend void hold(Man);
friend void touch(Man);
public:
Man(){ hand="My hand "; }
};
void hold(Man jobs){
jobs.hand+="is held";
cout << jobs.hand << endl;
}
void touch(Man jobs){
jobs.isTouch();
cout << jobs.hand << endl;
}
int main(){
Man jobs;
hold(jobs);
touch(jobs);
return 0;
}
Running /home/ubuntu/workspace/code34_friend.cpp
My hand is held
My hand is touched
เราสามารถสร้าง function ที่มีชื่อซ้ำกันได้โดยมีอินพุตที่ต่างกัน
using namespace std;
class printData
{
public:
void print(int i) {
cout << "Printing int: " << i << endl;
}
void print(double f) {
cout << "Printing float: " << f << endl;
}
void print(char* c) {
cout << "Printing character: " << c << endl;
}
};
int main(){
Vec2D A(3.0f, 4.0f),B(2.0f,1.0f),C,D;
cout<< "A = "<<A;
cout<< "B = "<<B;
D=C=A*10.0f + B*0.5f;
cout<< "C = "<< C;
cout<< "D = "<< D;
return 0;
}
5
500.263
Hello C++
คลาสอาจมีตัวกระทำที่เราสามารถกำหนดเองได้ เช่นเรามีคลาสที่ชื่อ Complex ที่เก็บจำนวนจินตภาพ โดยอาจจะมีตัวกระทำ +, - และ อื่นๆ รูปแแบการเขียนโปรแกรม คือ
type classname::operator+(arg-list) { // operations }
ตัวอย่างการทำ operator overloading
#include <iostream>
#include <math.h>
using namespace std;
class Vec2D{
private:
float x,y;
public:
Vec2D(){ x = 0.0f; y = 0.0f;}
Vec2D(float _x, float _y){
x = _x;
y = _y;
}
Vec2D operator - (Vec2D B){//subtraction
Vec2D temp;
temp.x = x - B.x;
temp.y = y - B.y;
return temp;
}
Vec2D operator + (Vec2D B){//addition
Vec2D temp;
temp.x = x + B.x;
temp.y = y + B.y;
return temp;
}
Vec2D operator = (Vec2D B){
x = B.x;
y = B.y;
return *this;
}
Vec2D operator * (float k){
Vec2D temp;
temp.x = k * x;
temp.y = k * y;
return temp;
}
friend ostream& operator<<(ostream& out, const Vec2D& A);
};
ostream& operator<<(ostream& out, const Vec2D& A) {
return out << "( "<<A.x << ", "<< A.y << " )\n";
}
int main(){
Vec2D A(3.0f, 4.0f),B(2.0f,1.0f),C;
cout<< "A = "<<A;
cout<< "B = "<<B;
cout<< "A * 10 + B * 0.5 = "<< A*10.0f + B*0.5f;
return 0;
}
Running /home/ubuntu/workspace/code425_operator.cpp
A = ( 3, 4 )
B = ( 2, 1 )
C = ( 31, 40.5 )
D = ( 31, 40.5 )
input/output stream จาก fstream สามารถใช้เพื่อการเขียนอ่านไฟล์ โดยอาจใช้ตามตัวอย่างนี้
#include <iostream>
#include <fstream>
using namespace std;
int main(){
//write file
ofstream myfile1 ( "myfile.txt" );
myfile1<<"Hello World!"<<endl;
myfile1<<"Class cs112 is awesome!!"<<endl;
myfile1.close();
//read file
ifstream myfile2 ( "myfile.txt" );
for (string line; getline(myfile2, line); ){
cout << line << endl;
}
myfile2.close();
return 0;
}
Running /home/ubuntu/workspace/code426_read_write_files.cpp
Hello World!
Class cs112 is awesome!!
An object or an instance created from a class can have a pointer as similar to other datatypes
In order to declare data tpye and initialize starting value. We can follow the pattern below. Please note that when we access a data member from pointer we use '->' not '.'.
#include<iostream>
using namespace std;
class Obj{
public:
int id;
void inc(){++id;}
};
int main(){
Obj a,b;
Obj *pa,*pb;
pa=&a;
pb=&b;
pa->id=5;
b.id=7;
cout<<"1 a.id="<<a.id<<", b.id="<<b.id<<endl;
pb->inc();
cout<<"2 a.id="<<a.id<<", b.id="<<b.id<<endl;
return 0;
}
Running /home/ubuntu/workspace/code71_class_pointer.cpp
1 a.id=5, b.id=7
2 a.id=5, b.id=8
operator and & work properly with object. In function input, '' means pasing by pointer and '&' means passing by reference.
#include<iostream>
using namespace std;
class Obj{
public:
int id;
};
void swap_id_reference(Obj &a, Obj &b){
int temp=a.id;
a.id=b.id;
b.id=temp;
}
void swap_id_pointer(Obj *a, Obj *b){
int temp=a->id;
a->id=b->id;
b->id=temp;
}
int main(){
Obj a,b;
a.id=0;
b.id=1;
cout<<"1 a.id="<<a.id<<", b.id="<<b.id<<endl;
swap_id_reference(a,b);
cout<<"2 a.id="<<a.id<<", b.id="<<b.id<<endl;
swap_id_pointer(&a,&b);
cout<<"3 a.id="<<a.id<<", b.id="<<b.id<<endl;
return 0;
}
Running /home/ubuntu/workspace/code71_class_pointer_init.cpp
1 a.id=0, b.id=1
2 a.id=1, b.id=0
3 a.id=0, b.id=1
In many cases, return a copy of an object may not appropriate because it take time and memory to cpy the object. It is recommended to not copy the object from function to outer scope. The solution is to pass pointer back to the outer scope as the following code.
#include<iostream>
using namespace std;
class Obj{
public:
int id;
Obj(int _id){id=_id;}
};
Obj* last_object(Obj* a, Obj* b){
if(a->id < b->id){
return b;
}else{
return a;
}
}
int main(){
Obj a(5),b(7);
Obj* c=last_object(&a,&b);
cout<<c->id;
}
Running /home/ubuntu/workspace/code73_return_pointer.cpp
7
the keyword 'const' makes the variable to be constant. However, location of the 'const' may have differnct meaning.
#include<iostream>
using namespace std;
void func( int * p){
int y;
p=&y;
*p=4;
}
int main(){
int x;
int *p=&x;
x=5;//init
func(p);
cout<<x;
}
Running /home/ubuntu/workspace/code741_constant_arg.cpp
5
#include<iostream>
using namespace std;
void func(const int * p){
int y;
p=&y;
*p=4;
}
int main(){
int x;
int *p=&x;
x=5;//init
func(p);
cout<<x;
}
Running /home/ubuntu/workspace/code741_constant_arg.cpp
/home/ubuntu/workspace/code741_constant_arg.cpp: In function ‘void func(const int*)’:
/home/ubuntu/workspace/code741_constant_arg.cpp:6:7: error: assignment of read-only location ‘* p’
*p=4;
^
#include<iostream>
using namespace std;
void func( int * const p ){
int y;
p=&y;
*p=4;
}
int main(){
int x;
int *p=&x;
x=5;//init
func(p);
cout<<x;
}
Running /home/ubuntu/workspace/code741_constant_arg.cpp
/home/ubuntu/workspace/code741_constant_arg.cpp: In function ‘void func(int*)’:
/home/ubuntu/workspace/code741_constant_arg.cpp:5:6: error: assignment of read-only parameter ‘p’
p=&y
^
Access | public | protected | private |
---|---|---|---|
Same class | yes | yes | yes |
Derived classes | yes | yes | no |
Outside classes | yes | no | no |
class Base {
private:
int MyPrivateInt=1;
protected:
int MyProtectedInt=2;
public:
int MyPublicInt=3;
};
class Derived : public Base {
public:
int foo1() { return MyPrivateInt;} // Won't compile!
int foo2() { return MyProtectedInt;} // OK
int foo3() { return MyPublicInt;} // OK
};
class Unrelated {
private:
Base B;
public:
int foo1() { return B.MyPrivateInt;} // Won't compile!
int foo2() { return B.MyProtectedInt;} // Won't compile
int foo3() { return B.MyPublicInt;} // OK
};
derived.foo2(): 2
derived.foo3(): 3
unrelated.foo3(): 3
class A{
public:
int x;
protected:
int y;
private:
int z;
};
class B : public A {
// x is public
// y is protected
// z is not accessible from B
};
class C : protected A {
// x is protected
// y is protected
// z is not accessible from C
};
// 'private' is default for classes
class D : private A {
// x is private
// y is private
// z is not accessible from D
};
ฟังก์ชัน getArea() ไม่สามารถเข้าถึงจาก main() เพราะการเข้าถึงถูกเปลี่ยนเป็น protectec ด้วยการสืบทอด
class Rect{
public:
int width,height;
Rect(){}
int getArea(){ return width*height; }
protected:
int key;
};
class Square: protected Rect{
public:
Square(int edge){
width=edge;
height=edge;
cout<<key<<endl; //ok
cout<<width<<endl; //ok
}
};
int main(){
Square mysq(5);
//cout<<"mysq.area() is "<<mysq.getArea()<<endl; //cannot access getArea()
return 0;
}
ถ้าไม่ใช้ scope "A::", B จะไม่สามารถเข้าถึง print() เพราะสืบทอดแบบ private เราไม่ควรใช้ pattern นี้ในการเขียนโปรแกรมเพราะอาจเกิดความสับสนเรื่องการเข้าถึง
#include<iostream>
using namespace std;
class A{
public:
void print(){cout<<"it's A"<<endl;}
};
class B:private A{
public:
void print(){
cout<<"it's B"<<endl;
A::print();
}
};
class C: private B{
public:
void print(){
cout<<"it's C"<<endl;
B::print();
}
};
int main(){
C c;
c.print();
return 0;
}
Running /home/ubuntu/workspace/code941_private_inheritance.cpp
it's C
it's B
it's A
ถ้าต้องการป้องกันการเข้าถึงจากนอกคลาส protected inheritance จะเข้าใจได้ง่ายมากกว่า private inheritance
#include<iostream>
using namespace std;
class A{
public:
void print1(){cout<<"it's A"<<endl;}
};
class B:protected A{
public:
void print2(){
cout<<"it's B"<<endl;
print1();
}
};
class C: public B{
public:
void print3(){
cout<<"it's C"<<endl;
print2();
}
};
int main(){
C c;
c.print3();
return 0;
}
Running /home/ubuntu/workspace/code941_private_inheritance.cpp
it's C
it's B
it's A
ถ้ามีการสืบถอดหลายชั้น ลำดับการสร้างและการทำลายจะเป็นอย่างไร
#include<iostream>
using namespace std;
class A{
public:
A(){
cout<<"Constructing A"<<endl;
}
~A(){
cout<<"Destructing A"<<endl;
}
};
class B:private A{
public:
B(){
cout<<"Constructing B"<<endl;
}
~B(){
cout<<"Destructing B"<<endl;
}
};
class C: private B{
public:
C(){
cout<<"Constructing C"<<endl;
}
~C(){
cout<<"Destructing C"<<endl;
}
};
int main(){
C c;
return 0;
}
Running /home/ubuntu/workspace/code950_sequenceConDeCon.cpp
Constructing A
Constructing B
Constructing C
Destructing C
Destructing B
Destructing A
สังเกตผลลัพธ์ในการสร้างและทำลาย a1 และ a2 โดยการสร้างเป็นไปตามลำดับซ้ายไปขวา ส่วนการทำลายจะเริ่มทำลายตัวที่ถูกสร้างหลังสุดก่อน
#include<iostream>
using namespace std;
class A{
public:
int id;
A(int _id=0){
id=_id;
cout<<"Constructing A "<<id<<endl;
}
~A(){cout<<"Destructing A "<<id<<endl;}
};
class B:public A{
public:
A a1,a2;
B():a1(1),a2(2){cout<<"Constructing B"<<endl;}
~B(){cout<<"Destructing B"<<endl;}
};
class C:public B{
public:
C(){cout<<"Constructing C"<<endl;}
~C(){cout<<"Destructing C"<<endl;}
};
int main(){
C c;
return 0;
}
Running /home/ubuntu/workspace/code950_sequenceConDeCon.cpp
Constructing A 0
Constructing A 1
Constructing A 2
Constructing B
Constructing C
Destructing C
Destructing B
Destructing A 2
Destructing A 1
Destructing A 0
In many cases the programmers want to secretly hide their codes from others. There are many ways to do this. One of the hidding techniques is separate compilation. For example, a programmer want to hide all codes in the class Obj, he can follow the example below.
First, he create two files. The header file
//Obj.h
class Obj{
public:
int x;
Obj(int);
void inc(int);
};
The cpp file
//Obj.cpp
#include "Obj.h"
Obj::Obj(int _x=0){
x=_x;
}
void Obj::inc(int y){
x+=y;
}
The main file
//main.cpp
#include "Obj.h"
#include<iostream>
using namespace std;
int main(){
Obj a(3);
a.inc(2);
cout<<a.x<<endl;
return 0;
}
Compilation
g++ -c Obj.cpp
g++ -c main.cpp
g++ -o final main.o Obj.o
./final
g++ -c Obj.cpp
g++ -o final main.cpp Obj.o
./final
--help Display this information
--version Display compiler version information
-time Time the execution of each subprocess
-std=<standard> Assume that the input sources are for <standard>
--sysroot=<directory> Use <directory> as the root directory for headers
and libraries
-B <directory> Add <directory> to the compiler's search paths
-v Display the programs invoked by the compiler
-E Preprocess only; do not compile, assemble or link
-S Compile only; do not assemble or link
-c Compile and assemble, but do not link
-o <file> Place the output into <file>
-Wa All option tells the compiler to show warnings
Options starting with -g, -f, -m, -O, -W, or --param are automatically
passed on to the various sub-processes invoked by g++. In order to pass
other options on to these processes the -W<letter> options must be used.
For bug reporting instructions, please see:
<http://gcc.gnu.org/bugs.html>.
C:\Users\Wasit>
#include<iostream>
using namespace std;
class Alpha{
public:
Alpha(){ cout<<"construct Alpha\n";}
~Alpha(){ cout<<"destroy Alpha\n";}
};
class Beta{
public:
Beta(){ cout<<"construct Beta\n";}
~Beta(){ cout<<"destroy Beta\n";}
};
class Chi: public Beta, public Alpha{ //private by default
public:
Chi(){ cout<<"construct Chi\n";}
~Chi(){ cout<<"destroy Chi\n";}
};
int main(){
Chi c;
return 0;
}
Running /home/ubuntu/workspace/code10_20_multi_inher.cpp
construct Beta
construct Alpha
construct Chi
destroy Chi
destroy Alpha
destroy Beta
ย้อนกลับไปดู section 9
#include<iostream>
using namespace std;
class Base{
public:
Base(int x, int y){cout<<x<<", "<<y<<"\n";}
~Base(){cout<<"Base destructs\n";}
};
int main(){
Base *b=new Base[2]{{1,2},{3,4}};
delete[] b;
return 0;
}
copy address of a pointer
copy all data members
There are three types of polymorphism
virtual function ทำให้ polygon มีฟังก์ชัน area() ซึ่งจะกำหนดอีกครั้งหลังการสืบทอด
#include <iostream>
using namespace std;
class Polygon {
protected:
int width, height;
public:
void set_values (int a, int b)
{ width=a; height=b; }
virtual int area ()
{ return 0; }
};
class Rectangle: public Polygon {
public:
int area ()
{ return width * height; }
};
class Triangle: public Polygon {
public:
int area ()
{ return (width * height / 2); }
};
int main () {
Rectangle rect;
Triangle trgl;
Polygon poly;
Polygon * ppoly1 = ▭
Polygon * ppoly2 = &trgl;
Polygon * ppoly3 = &poly;
ppoly1->set_values (4,5);
ppoly2->set_values (4,5);
ppoly3->set_values (4,5);
cout << ppoly1->area() << '\n';
cout << ppoly2->area() << '\n';
cout << ppoly3->area() << '\n';
return 0;
}
20 10 0
#include <iostream>
class Base {
public:
virtual void f() {
std::cout << "base\n";
}
};
struct Derived : public Base {
public:
void f() override { // 'override' is optional
std::cout << "derived\n";
}
};
int main()
{
Base b;
Derived d;
// virtual function call through reference
Base& br = b; // the type of br is Base&
Base& dr = d; // the type of dr is Base& as well
br.f(); // prints "base"
dr.f(); // prints "derived"
// virtual function call through pointer
Base* bp = &b // the type of bp is Base*
Base* dp = &d // the type of dp is Base* as well
bp->f(); // prints "base"
dp->f(); // prints "derived"
// non-virtual function call
br.Base::f(); // prints "base"
dr.Base::f(); // prints "base"
}
difinition from cambridge dictionary override verb (CONTROL) : to take control over something, especially in order to change the way it operates.
An abstract class is unable to construct an object. The "=0" indicates a null function. Therefore, the function member is incomplete requiring further implementaion.
#include <iostream>
using namespace std;
class Base{
public:
virtual void print()=0;
};
class Derived:public Base{
public:
void print(){
cout<<"From Derived"<<endl;
}
};
int main(){
Base * pb= new Derived();
//Base * pb2= new Base(); //cannot allocate an object of abstract type 'Base'
pb->print();
return 0;
}
#include <iostream>
using namespace std;
class Base{
public:
Base(){cout<<"Base Constructor Called\n";}
virtual ~Base(){cout<<"Base Destructor called\n";}
};
class Derived:public Base{
public:
Derived(){cout<<"Derived constructor called\n";}
~Derived(){cout<<"Derived destructor called\n";}
};
int main(){
base* b = new Derived;
delete b;
}
#include <iostream>
using namespace std;
class Base{
public:
int *ptr;
Base(){
ptr=new int(1);
}
Base(const Base &b){//copy constructor overloading
ptr=new int(*(b.ptr));
}
~Base(){
delete ptr;
}
};
int main(){
Base B1, B2=B1;
cout<<B1.ptr<<" value:"<<*(B1.ptr)<<endl;
cout<<B2.ptr<<" value:"<<*(B2.ptr)<<endl;
while (1){
Base B3;
}
return 0;
}
#include <iostream>
using namespace std;
class Base{
public:
Base(){
cout<<"base construct\n";
}
virtual ~Base(){
cout<<"base destruct\n";
}
};
class Derived: public Base{
public:
int *ptr;
Derived(){
ptr=new int(1);
cout<<"derived construct\n";
}
Derived(const Derived &d){//copy constructor overloading
ptr=new int(*(d.ptr));
cout<<"derived copy construct "<<ptr<<endl;
}
~Derived(){
cout<<"derived destruct "<<ptr<<endl;
delete ptr;
}
};
int main(){
Derived D1;
while (1){
Base *D2 = new Derived(D1);
delete D2;
}
return 0;
}
#include<iostream>
using namespace std;
class Base{
public:
Base(){cout<<"Base constructs\n";}
virtual ~Base(){cout<<"Base destructs\n";}
};
class Derived: public Base{
public:
int *pt;
Derived(){
pt=new int[100];
cout<<"Derived constructs\n";
}
~Derived(){
delete[] pt;
cout<<"Derived destructs\n";
}
};
int main(){
while(1){
//Base b = Base(); //works
//Derived d= Derived();
Base *b = new Derived[3];
delete[] (Derived*)b;//if there is no 'virtual', delete only base object
}
return 0;
}