Getting used to object oriented programming?

objectusedto

I think I am now having less difficulty than before.

/* main.cpp */
#include "MyWindow.h"
#include "MyArea.h"
#include "MyButton.h"
#include <gtkmm.h>

int main(int argc, char** argv)
{
   Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

   MyWindow win;

   win.set_title("Object Oriented?");
   win.set_default_size(400, 400);
   win.show_all_children();

   return app->run(win);
}
/* MyWindow.h */
#ifndef MYWINDOW_H_
#define MYWINDOW_H_
#include "MyArea.h"
#include "MyButton.h"
#include <gtkmm.h>

class MyWindow : public Gtk::Window {
public:
	MyWindow();
	virtual ~MyWindow();
protected:
	Gtk::Box m_box;
	MyArea   m_area;
	MyButton m_buttons;
};

#endif /* MYWINDOW_H_ */
/* MyWindow.cpp */
#include "MyWindow.h"
#include <gtkmm.h>

MyWindow::MyWindow() : m_box(Gtk::ORIENTATION_VERTICAL) {
	add(m_box);
	m_box.pack_start(m_area);
	m_box.pack_start(m_buttons, false, true);
}

MyWindow::~MyWindow() {
}
/* MyArea.h */
#ifndef MYAREA_H_
#define MYAREA_H_

#include <gtkmm.h>

class MyArea : public Gtk::DrawingArea
{
public:
  MyArea();
  virtual ~MyArea();

protected:
  virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr);
 };

#endif /* MYAREA_H_ */
/* MyArea.cpp */
#include "MyArea.h"
#include <gtkmm.h>
#include <cairomm/context.h>
#include <complex>
using namespace std;

double x_min =  -1.5;
double x_max =   0.5;
double y_min =  -1.0;
double y_max =   1.0;

MyArea::MyArea()
{
}

MyArea::~MyArea()
{
}

bool MyArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
{
  Gtk::Allocation allocation = get_allocation();
  const int width  = allocation.get_width();
  const int height = allocation.get_height();

  cr->set_line_width(1.0);

  double large =  100.0;
  int    kmax  = 50;

  for(int i=0;i<width;i++) {
	  for(int j=0;j<height;j++) {
		  double x=x_min+(x_max-x_min)*(double)i/(double)width;
		  double y=y_min+(y_max-y_min)*(double)j/(double)height;
		  complex<double> z=0.0;
		  complex<double> w=complex<double>(x,y);
		  int count=0;
		  for(int k=0;k<kmax;k++) {
			  z=z*z+w;
			  if(abs(z)>large) {
				  count = k;
				  break;
			  }
		  }
		  if(count) {
			  cr->set_source_rgba(0.9,(count%10)/10.0,0.3,1.0);
		  } else {
			  cr->set_source_rgba(0.0,0.2,0.7,1.0);
		  }
		  cr->rectangle((double)i, (double)j, 1.0, 1.0);
		  cr->stroke();
	  }
  }
  return true;
}
/* MyButton.h */
#ifndef MYBUTTON_H_
#define MYBUTTON_H_
#include <gtkmm.h>

class MyButton : public Gtk::Box {
public:
	MyButton();
	virtual ~MyButton();
protected:
	void on_button1_clicked();
	void on_button2_clicked();
	void on_button3_clicked();
	void on_button4_clicked();
	void on_button5_clicked();
	void on_button6_clicked();
	Gtk::Button m_button1;
	Gtk::Button m_button2;
	Gtk::Button m_button3;
	Gtk::Button m_button4;
	Gtk::Button m_button5;
	Gtk::Button m_button6;
};

#endif /* MYBUTTON_H_ */
/* MyButton.cpp */
#include "MyButton.h"

extern  double x_min;
extern  double x_max;
extern  double y_min;
extern  double y_max;

MyButton::MyButton() : m_button1("enlarge"), m_button2("shrink"), m_button3("left"), m_button4("right"), m_button5("up"), m_button6("down") {
	pack_start(m_button1);
	pack_start(m_button2);
	pack_start(m_button3);
	pack_start(m_button4);
	pack_start(m_button5);
	pack_start(m_button6);
	m_button1.signal_clicked().connect(sigc::mem_fun(*this, &MyButton::on_button1_clicked) );
	m_button2.signal_clicked().connect(sigc::mem_fun(*this, &MyButton::on_button2_clicked) );
	m_button3.signal_clicked().connect(sigc::mem_fun(*this, &MyButton::on_button3_clicked) );
	m_button4.signal_clicked().connect(sigc::mem_fun(*this, &MyButton::on_button4_clicked) );
	m_button5.signal_clicked().connect(sigc::mem_fun(*this, &MyButton::on_button5_clicked) );
	m_button6.signal_clicked().connect(sigc::mem_fun(*this, &MyButton::on_button6_clicked) );
}

MyButton::~MyButton() {
}

void MyButton::on_button1_clicked() {
	double xc = (x_min + x_max) / 2.0;
	double yc = (y_min + y_max) / 2.0;
	double xs = (x_max - xc) * 0.75;
	double ys = (y_max - yc) * 0.75;
	x_min = xc - xs;
	x_max = xc + xs;
	y_min = yc - ys;
	y_max = yc + ys;
	get_window()->invalidate(true);
}

void MyButton::on_button2_clicked() {
	double xc = (x_min + x_max) / 2.0;
	double yc = (y_min + y_max) / 2.0;
	double xs = (x_max - xc) / 0.75;
	double ys = (y_max - yc) / 0.75;
	x_min = xc - xs;
	x_max = xc + xs;
	y_min = yc - ys;
	y_max = yc + ys;
	get_window()->invalidate(true);
}

void MyButton::on_button3_clicked() {
	double xc = (x_min + x_max) / 2.0;
	double xs = (x_max - xc) * 0.75;
	x_min += xs;
	x_max += xs;
	get_window()->invalidate(true);
}

void MyButton::on_button4_clicked() {
	double xc = (x_min + x_max) / 2.0;
	double xs = (x_max - xc) * 0.75;
	x_min -= xs;
	x_max -= xs;
	get_window()->invalidate(true);
}

void MyButton::on_button5_clicked() {
	double yc = (y_min + y_max) / 2.0;
	double ys = (y_max - yc) * 0.75;
	y_min += ys;
	y_max += ys;
	get_window()->invalidate(true);
}

void MyButton::on_button6_clicked() {
	double yc = (y_min + y_max) / 2.0;
	double ys = (y_max - yc) * 0.75;
	y_min -= ys;
	y_max -= ys;
	get_window()->invalidate(true);
}

Mandelbrot Set

mandelbrot

bool MyArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
{
  Gtk::Allocation allocation = get_allocation();
  const int width = allocation.get_width();
  const int height = allocation.get_height();

  cr->set_line_width(1.0);
 
  double x_min =  -1.5;
  double x_max =   0.5;
  double y_min =  -1.0;
  double y_max =   1.0;
  double large =  10.0;
  int    kmax  = 500;

  for(int i=0;i<width;i++) {
	  for(int j=0;j<height;j++) {
		  double x=x_min+(x_max-x_min)*(double)i/(double)width;
		  double y=y_min+(y_max-y_min)*(double)j/(double)height;
		  complex<double> z=0.0;
		  complex<double> w=complex<double>(x,y);
		  int count=0;
		  for(int k=0;k<kmax;k++) {
			  z=z*z+w;
			  if(abs(z)>large) {
				  count = k;
				  break;
			  }
		  }
		  if(count) {
			  cr->set_source_rgba(0.9,(count%10)/10.0,0.3,1.0);
		  } else {
			  cr->set_source_rgba(0.0,0.2,0.7,1.0);
		  }
		  cr->rectangle((double)i, (double)j, 1.0, 1.0);
		  cr->stroke();
	  }
  }
  return true;
}

Object-oriented?

ball4

Here is a toy program, in which some balls are bouncing in a box and repulsing each other with the electrostatic force, or by the Coulomb potential.

I do not know if the program is reasonably well written or very bad, but surely the line 10 in MyArea.cpp is ugly.

/* main.cpp */
#include <gtkmm/application.h>
#include <gtkmm/window.h>
#include "MyArea.h"
#include "MyBall.h"

int main(int argc, char** argv)
{
   Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
   Gtk::Window window;
   window.set_title("Animation Test");
   window.set_default_size(400, 400);
   MyArea area;
   window.add(area);
   area.show();
   return app->run(window);
}
/* MyArea.h */
#ifndef MYAREA_H_
#define MYAREA_H_
#include <gtkmm/drawingarea.h>

class MyArea : public  Gtk::DrawingArea {
public:
	MyArea();
	virtual ~MyArea();
protected:
	virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr);
	bool on_timeout();
};

#endif /* MYAREA_H_ */
/* MyArea.cpp */
#include <vector>
#include <cmath>
#include <cairomm/context.h>
#include <glibmm/main.h>
#include "MyArea.h"
#include "MyBall.h"
using namespace std;

vector<MyBall> particle(4);

MyArea::MyArea() {
	Glib::signal_timeout().connect( sigc::mem_fun(*this, &MyArea::on_timeout), 10 );

	for(unsigned int i=0;i<particle.size();i++) {
		double center = (0 + particle.size() - 1) / 2.0;
		double f = (i-center) / particle.size();
		particle[i].Set(0.04, 1.0, f+0.5, 0.8*f, -0.4, 0.0, 0.001*f);
	}
}

MyArea::~MyArea() {
}

void Force(MyBall& b1, MyBall& b2) {
	double x1 = b1.Getx();
	double y1 = b1.Gety();
	double x2 = b2.Getx();
	double y2 = b2.Gety();
	double dist_sq = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
	double dist    = sqrt(dist_sq);
	double force   = 0.000001 / dist_sq;
	double force_x = -force * (x2 - x1) / dist;
	double force_y = -force * (y2 - y1) / dist;
	b1.Changevx( force_x);
	b2.Changevx(-force_x);
	b1.Changevy( force_y);
	b2.Changevy(-force_y);
}

bool MyArea::on_timeout() {
	static int count = 0;
	if(count++ % 5 == 0) {	get_window()->invalidate(true); }

	for(unsigned int i=0;i<particle.size();i++) {
		for(unsigned int j=0;j<particle.size();j++) {
			if(i != j) Force(particle[i], particle[j]);
		}
	}

	for(unsigned int i=0;i<particle.size();i++) {
		particle[i].Move();
	}

    return true;
}

bool MyArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) {

	Gtk::Allocation allocation = get_allocation();
	const int width  = allocation.get_width();
	const int height = allocation.get_height();

	cr->scale(width, height);
	cr->translate(0.5, 0.5);
	cr->set_source_rgba(0.9, 0.9, 0.8, 1.0);
	cr->paint();

	for(unsigned int i=0;i<particle.size();i++) {
		particle[i].Display(cr);
	}

	return true;
}
/* MyBall.h */
#ifndef MYBALL_H_
#define MYBALL_H_
#include <cairomm/context.h>
class MyBall {
public:
			 MyBall ();
	         MyBall (double, double, double, double, double, double);
	virtual ~MyBall ();
	void     Set    (double, double, double, double, double, double);
	void     Display(const Cairo::RefPtr<Cairo::Context>&);
	double   Getx() const;
	double   Gety() const;
	double   Getw() const;
	void     Changevx(double);
	void     Changevy(double);
	void     Move();
private:
	double m_r;
	double m_c;
	double m_x;
	double m_y;
	double m_vx;
	double m_vy;
};

#endif /* MYBALL_H_ */
/* MyBall.cpp */
#include <cairomm/context.h>
#include "MyBall.h"

MyBall::MyBall() : m_r {0.1}, m_c {0.5}, m_x {0.0}, m_y {0.0}, m_vx {0.0}, m_vy {0.0} {
}

MyBall::~MyBall() {
}

double MyBall::Getx() const {
	return m_x;
}

double MyBall::Gety() const {
	return m_y;
}

void MyBall::Changevx(double v) {
	m_vx += v;
}

void MyBall::Changevy(double v) {
	m_vy += v;
}

void MyBall::Display(const Cairo::RefPtr<Cairo::Context>& cr) {
	cr->arc(m_x, m_y, m_r, 0.0, 2.0*M_PI);
	cr->set_source_rgba(0.0, m_c, 1.0-m_c, 1.0);
	cr->fill_preserve();
	cr->set_source_rgba(0.0, 0.0, 0.0, 1.0);
	cr->set_line_width(0.01);
	cr->stroke();
}

void MyBall::Move() {
	if(m_x>0.5 || m_x<-0.5) { m_vx = -m_vx; }
	if(m_y>0.5 || m_y<-0.5) { m_vy = -m_vy; }
	m_x += m_vx;
	m_y += m_vy;
}

void MyBall::Set(double r, double c, double x, double y, double vx, double vy) {
	m_r = r;
	m_c = c;
	m_x = x;
	m_y = y;
	m_vx = vx;
	m_vy = vy;
}

No Data Structures (4)

datastructure4

Now, it is a little bit better using the C++ STL (Standard Template Library).

  vector< vector<string> > label
  { {"CW", "CW-R", "LSB", "USB"},
	{"DSP FIL 1", "DSP FIL 2", "DSP FIL 3"},
	{"DSP SHARP", "DSP SOFT"},
	{"IF FIL 1", "IF FIL 2", "IF FIL 3"},
	{"PRE-AMP1", "PRE-AMP2", "ATT 20dB", "BOTH OFF"},
	{"AGC FAST", "AGC MID", "AGC SLOW"},
	{"ANT 1", "ANT 2"},
	{"BKIN OFF", "BKIN ON", "BKIN FULL"},
  };

  int index = 0;
  for(unsigned int i=0;i<label.size();i++) {
	m_Box10[i].set_orientation(Gtk::ORIENTATION_VERTICAL);
    m_Box10[i].set_border_width(3);
	for(unsigned int j=0;j<label.at(i).size();j++) {
      m_RadioButton[index].set_label(label[i][j]);
      if(j==0) {
        m_RadioButton[index].set_active();
        m_group[i] = m_RadioButton[index].get_group();
      } else {
        m_RadioButton[index].set_group(m_group[i]);
      }
      m_Box10[i].pack_start(m_RadioButton[index], FALSE, FALSE, 0);
      m_RadioButton[index].signal_clicked().connect(sigc::bind<gint> (mem_fun(*this, &RadioButtons::on_button_clicked9), index));
      index++;
    }
    m_Box1.pack_start(m_Box10[i]);
    m_Box1.pack_start(m_VSeparator[i]);
  }
  m_Box1.pack_start(m_Button_Quit);

You see that initializing a vector label conveys all the information necessary for placing the radio buttons on the screen, and there is no additional information in the source code concerning the layout.

Learning Curve

Alanf777_Lcd_fig08

http://en.wikipedia.org/wiki/Learning_curve

When you start learning something without any prior knowledge, the learning curve must be very steep.

#include <vector>
#include "Myclass.h"

int main() {
	vector<Myclass> orange(2);
	for(auto x : orange) {
		x.display();
	}
	return 0;
}

So how many times do you expect to have your constructor and destructor called respectively?

constructor. 
constructor. 
  t1 = 1
  t2 = mystring
destructor. 
  t1 = 1
  t2 = mystring
destructor. 
destructor. 
destructor. 

Looks funny? The constructor is called twice, while the destructor is called four times.

Myclass:: Myclass(const Myclass &obj) {
	cout << "copy constructor. \n";
	t1 = obj.t1;
	t2 = obj.t2;
}

If you add a user defined copy constructor, you will know that the destructor is called as many times as the constructors.

constructor. 
constructor. 
copy constructor. 
  t1 = 1
  t2 = mystring
destructor. 
copy constructor. 
  t1 = 1
  t2 = mystring
destructor. 
destructor. 
destructor. 

So far, so good. I am learning quickly as is expected. However, I have a question. Why my learning curve is so shallow with my CW?

How long does it take to learn C++?

vector

The answer available at http://www.stroustrup.com/bs_faq.html#How-long is:
That depends on what you mean by “learning.” If you are a C programmer you can learn enough C++ to make you more effective at C-style programming in a day.

He also says in http://www.stroustrup.com/learn.html that
Most experienced programmers I have talked with quotes times from a half year to one and a half year for becomming really comfortable with C++ and the key data abstraction and object- oriented techniques it supports.

It will be two years next week since I restarted ham radio, so it is almost time that I feel comfortable with CW conversation.

10
11
12
10
11
12
10
11
12