If Cwin does not explicitly implement its own assignment-operator (which your first example does not), the compiler will generate a default implementation that simply copies member values as-is from one object to another.  That is why your output said the same thing for both objects - the win1 = win2 statement using the default assigment-operator implementation merely overwrote the pointer value of win1.title with the pointer value of win2.title, and thus both title members were left pointing at the same memory block, which the subsequent win1.set_data() statement populated with data.
To do what you are asking, you should change title to use std::string instead of char*, let it handle the copying for you.  It will work fine with the compiler's default copy-constructor and assignment-operator implementations (unless you have other data that you need to make manual copies of):
#include <string>
Class Cwin
{
private:
  char id;
  std::string title;
public:
  Cwin()
    : id('D'), title("default") {}
  Cwin(char i, const std::string &text)
    : id(i), title(text) {}
  void set_data(char i, const std::string &text)
  {
    id = i;
    title = text;
  }
  void show()
  {
    std::cout << id << " : " << title << std::endl;
  }
};
But, if you need to use char* directly, you must implement the copy-constructor and assignment-operator properly to ensure your title data gets copied correctly, eg:
#include <cstring> 
Class Cwin
{
private:
  char id, *title ;
public:
  Cwin()
    : id(0), title(NULL)
  {
    set_data('D', "default");
  }
  Cwin(char i, char *text)
    : id(0), title(NULL)
  {
    set_data(i, text);
  }
  Cwin(const Cwin &win)
    : id(0), title(NULL)
  {
    set_data(win.id, win.title);
  }
  ~Cwin()
  {
    delete[] title;
  }
  Cwin& operator=(const Cwin &win)
  {
    if (this != &win)
      set_data(win.id, win.title);
    return *this;
  }
  void set_data(char i, char *text)
  {
    int len = std::strlen(text);
    char *newtitle = new char[len+1];
    std::strcpy(newtitle, text);
    delete[] title;
    title = newtitle;
    id = i;
  }
  void show()
  {
    std::cout << id << " : " << title << std::endl;
  }
};
Any time you have to manually allocate memory in a constructor and free it in a destructor, chances are good that you will also need to copy that data in a copy-constructor and assignment-operator as well.  Read up about the The rule of three/five/zero.  In this situation, CWin needed to follow the Rule of three in order to function properly with char*, but can follow the Rule of zero when using std::string.  You should always strive to write code that follows the Rule of zero whenever possible, it makes things easier to manage.