一、拷贝构造函数(clone constructor)和拷贝赋值运算符
(p.s.请特别注意:操作符'='! 操作符'='出现在声明语句中是初始化操作符; 操作符'='出现在赋值语句中是赋值操作符。
编译器对他们(初始化操作符'='和赋值操作符'=')的处理方式不一样,
虽然同样都是符号'='。请特别注意区分同一词法记号在不同上下文中被编译处理的方式是不一样的。另外,实质上拷贝构造函数实现了初始化操作符,拷贝赋值
函数实现了赋值操作符)
(p.s.拷贝构造函数重载了初始化操作符,同时也重载了构造函数,拷贝赋值函数是重载了赋值操作符)
1.拷贝构造:用一个已有的对象,构造和它同类型的副本对象——克隆。
2.形如
class X {
X (const X& that) { ... }
};
的构造函数称为拷贝构造函数。如果一个类没有定义拷贝构造函数,系统会提供一个缺省拷贝构造函数。缺省拷贝构造函数对于基本类型的成员变量,按字节复制,对于类类型的成员变量,调用相应类型的拷贝构造函数。
3.在某些情况就下,缺省拷贝构造函数只能实现浅拷贝,如果需要获得深拷贝的复制效果,就需要自己定义拷贝构造函数。
4.形如
class X {
X& operator= (const X& that) {
...
}
};
的成员函数称为拷贝赋值运算符函数。如果一个类没有定义拷贝赋值运算符函数,系统会提供一个缺省拷贝赋值运算符函数。缺省拷贝赋值运算符函数对于基本类型的成员变量,按字节复制,对于类类型的成员变量,调用相应类型的拷贝赋值运算符函数。
5.在某些情况就下,缺省拷贝赋值运算符函数只能实现浅拷贝,如果需要获得深拷贝的复制效果,就需要自己定义拷贝赋值运算符函数。
1 /* 2 * 拷贝构造 3 * * */ 4 #include <iostream> 5 using namespace std; 6 class Integer { 7 public: 8 Integer (int data = 0) : m_data (data) {} 9 void print (void) const { 10 cout << m_data << endl; 11 } 12 /* 13 Integer (const Integer& that) : 14 m_data (that.m_data) { 15 cout << "拷贝构造" << endl; 16 } 17 */ 18 private: 19 int m_data; 20 }; 21 void foo (Integer i) { 22 i.print (); 23 } 24 Integer bar (void) { 25 Integer i; 26 return i; 27 } 28 int main (void) { 29 Integer i1 (10); 30 i1.print (); 31 Integer i2 (i1); // 拷贝构造 32 i2.print (); 33 Integer i3 = i1; // 拷贝构造//这里的等号是初始化操作符号。 34 i3.print (); 35 // Integer i4 (10, 20); 36 cout << "调用foo()函数" << endl; 37 foo (i1); 38 cout << "调用bar()函数" << endl; 39 Integer i4 (bar ());//编译器gcc会对本句作优化。直接将匿名Integer对象命名为i4. 40 i4 = i3;//这里的等号是赋值操作符号。//这时候就需要重载赋值操作符号'=' 41 return 0; 42 }
1 /* 2 *拷贝赋值 3 * * */ 4 #include <iostream> 5 using namespace std; 6 class Integer { 7 public: 8 Integer (int data) : m_data (new int (data)) {} 9 ~Integer (void) { 10 if (m_data) { 11 delete m_data; 12 m_data = NULL; 13 } 14 } 15 void print (void) const { 16 cout << *m_data << endl; 17 } 18 Integer (const Integer& that) : 19 m_data (new int (*that.m_data)) {} 20 void set (int data) { 21 *m_data = data; 22 } 23 Integer& operator= (const Integer& that) { 24 // 因为考虑要重新分配存储空间,所以对于自赋值要特别处理,防止产生野指针出现 25 // 防止自赋值 26 if (&that != this) { 27 // 释放旧资源 28 delete m_data; 29 // 分配新资源 30 m_data = new int (*that.m_data); 31 // 拷贝新数据 32 } 33 // 返回自引用 34 return *this; 35 } 36 /*编译器给的缺省拷贝赋值函数 37 Integer& operator=(const Integer& that) 38 { 39 m_data = that.m_data; 40 } 41 */ 42 43 private: 44 int* m_data; 45 }; 46 int main (void) { 47 Integer i1 (10); 48 i1.print (); 49 Integer i2 = i1;//这里的等号是初始化操作符//调用的是构造函数(拷贝构造函数) 50 i2.print (); 51 i2.set (20); 52 i2.print (); 53 i1.print (); 54 Integer i3 (30); 55 i3.print (); // 30 56 i3 = i1; // 拷贝赋值//这里的等号是赋值操作符//调用的是拷贝赋值函数(就是赋值操作符=的重载函数, 就是i3.operator= (i1); 57 i3.print (); // 10 58 i3.set (40); 59 i3.print (); // 40 60 i1.print (); // 10 61 /* 62 int a = 10, b = 20, c = 30; 63 (a = b) = c; 64 cout << a << endl; 65 */ 66 (i3 = i1) = i2; 67 // i3.operator=(i1).operator=(i2); 68 i3.print (); 69 i3 = i3; 70 i3.print (); 71 return 0; 72 }
二、静态成员
1.静态成员变量和静态成员函数是属于类的而非属于对象。
2.静态成员变量,为多个对象所共享,只有一份实例,可以通过对象访问也可以通过类访问,必须在类的外部定义并初始化。静态成员变量本质上与全局变量并没有区别,只是多了类作用域的约束,和访控属性的限制。
class Account {
private: string m_name;
double m_balance;
static double m_rate;
};
3.静态成员函数,没有this指针,无法访问非静态成员。
1 /* 2 *静态成员举例 3 * */ 4 #include <iostream> 5 using namespace std; 6 class A { 7 public: 8 static int m_i; 9 static void foo (void) { 10 cout << "foo:" << m_i << endl; 11 // m_d = 3.14; 12 // bar (); 13 } 14 double m_d; 15 void bar (void) { 16 m_i = 1000; 17 foo (); 18 } 19 }; 20 int A::m_i = 1; 21 int main (void) { 22 A::m_i = 10; 23 A a1, a2; 24 cout << ++a1.m_i << endl; 25 cout << a2.m_i << endl; 26 A::foo (); 27 a1.foo (); 28 a1.bar (); 29 return 0; 30 }
4.单例模式:可以创建该类的对象,但是只能创建一个。
单例模式可以通过将构造函数私有化(访控属性为private)来实现。具体的就是将构造函数等各类有可能创建对象的函数一律私有化,然后提供一个先于任何对象的接口来创建对象。
举例:
1 #include <iostream> 2 using namespace std; 3 // 饿汉方式. 4 ////以下实现方式称为"饿汉方式",特征是当进程结束后就会被释放掉,不管当前有多少进程在使用当前对象 5 class Singleton { 6 public: 7 static Singleton& getInst (void) { 8 return s_inst; 9 } 10 private: 11 Singleton (void) {} 12 Singleton (const Singleton&); 13 static Singleton s_inst; 14 }; 15 Singleton Singleton::s_inst; 16 int main (void) { 17 //Singleton s;//ERROR 18 Singleton& s1 = Singleton::getInst (); 19 Singleton& s2 = Singleton::getInst (); 20 Singleton& s3 = Singleton::getInst ();///显然s1,s2是同一个对象。 21 cout << &s1 << ' ' << &s2 << ' ' << &s3 << endl; 22 return 0; 23 }
1 #include <iostream> 2 using namespace std; 3 // 懒汉方式 4 class Singleton { 5 public: 6 static Singleton& getInst (void) { 7 if (! m_inst) 8 m_inst = new Singleton; 9 ++m_cn; 10 return *m_inst; 11 } 12 void releaseInst (void) { 13 if (m_cn && --m_cn == 0) 14 delete this; 15 } 16 private: 17 Singleton (void) { 18 cout << "构造:" << this << endl; 19 } 20 Singleton (const Singleton&); 21 ~Singleton (void) { 22 cout << "析构:" << this << endl; 23 m_inst = NULL; 24 } 25 static Singleton* m_inst; 26 static unsigned int m_cn; 27 }; 28 Singleton* Singleton::m_inst = NULL; 29 unsigned int Singleton::m_cn = 0; 30 int main (void) { 31 Singleton& s1 = Singleton::getInst (); 32 Singleton& s2 = Singleton::getInst (); 33 Singleton& s3 = Singleton::getInst (); 34 cout << &s1 << ' ' << &s2 << ' ' << &s3 << endl; 35 s3.releaseInst (); 36 s2.releaseInst (); 37 s1.releaseInst (); 38 return 0; 39 }
三、成员指针
Student s;
string* p = &s.m_name; // 不是
Student s2;
(一)成员变量指针
1.定义
成员变量类型 类名::*指针变量名;
string Student::*pname;
int Student::*page;
2.初始化/赋值
指针变量名 = &类名::成员变量名
pname = &Student::m_name;
page = &Student::m_age;
3.使用
对象.*指针变量名
对象指针->*指针变量名
Student s, *p = &s;
s.*pname = "张飞";
cout << p->*page << endl;
(p.s.成员对象指针变量里面存放的是偏移地址。)
(p.s.静态成员对象指针变量里面存放的是直接地址。)
(二)成员函数指针
(post script:C语言函数函数名称代表着函数首地址,
#include <stdio.h>
void foo(int*);
main()
{
int a = 0;
foo(a);
void (*pfn1)(int*) = foo;//C语言C89之后允许这样定义函数指针
pfn1(a);
void (*pfn2)(int*) = &foo;//C语言C89之前确定的函数指针定义方法。这种方法被C++沿用
(*pfn2)(a);
printf("pfn1 = %p\n", pfn1);
printf("pfn2 = %p\n", pfn2);
printf("*pfn2 = %p\n", *pfn2);
}
)
1.定义
成员函数返回类型 (类名::*指针变量名) (参数表)
void (Student::*plearn) (const string&) const;
2.初始化/赋值
指针变量名 = &类名::成员函数名;
plearn = &Stduent::learn;
3.使用
(对象.*指针变量名) (实参表);
(对象指针->*指针变量名) (实参表);
(s.*plearn) ("C++");
//编译后就是(*plearn)(&s, "C++");
//plearn存放了函数地址,*plearn间接访问到函数起始地址,s作为实际参数传递给this
(p->*plearn) ("UNIX");
(p.s.成员函数指针变量直接存放的就是地址,而非偏移地址。)
(p.s.静态成员函数指针变量里面存放的是直接地址。)
1 /* 2 *成员指针举例 3 */ 4 #include <iostream> 5 #include <cstring> 6 using namespace std; 7 class Student { 8 public: 9 Student (const string& name, int age) : 10 m_name (name), m_age (age) {} 11 double m_weight; 12 string m_name; 13 int m_age; 14 void learn (const string& lesson) const { 15 cout << "我在学" << lesson << endl; 16 } 17 static void hello (void) { 18 cout << "你好!" << endl; 19 } 20 }; 21 int main (void) { 22 string Student::*pname = &Student::m_name; 23 void* pv; 24 memcpy (&pv, &pname, 4); 25 cout << pv << endl; 26 int Student::*page = &Student::m_age; 27 memcpy (&pv, &page, 4); 28 cout << pv << endl; 29 Student s ("张飞", 25), *p = &s; 30 cout << s.*pname << endl; 31 cout << p->*page << endl; 32 Student s2 ("赵云", 22); 33 cout << s2.*pname << endl; 34 void (Student::*plearn) (const string&) const = 35 &Student::learn; 36 (s.*plearn) ("C++"); 37 (p->*plearn) ("UNIX"); 38 void (*phello) (void) = Student::hello; 39 phello (); 40 return 0; 41 }
1 /* 2 * String练习 3 */ 4 #include <iostream> 5 #include <cstring> 6 using namespace std; 7 class String { 8 public: 9 String (const char* str = NULL) { 10 m_str = new char[strlen(str?str:"")+1]; 11 strcpy (m_str, str ? str : ""); 12 } 13 ~String (void) { 14 if (m_str) { 15 delete[] m_str; 16 m_str = NULL; 17 } 18 } 19 String (const String& that) : 20 m_str (strcpy ( 21 new char[strlen(that.m_str)+1], 22 that.m_str)) {} 23 /* 菜鸟 24 void operator= (const String& that) { 25 m_str = new char[strlen(that.m_str)+1]; 26 strcpy (m_str, that.m_str); 27 }*/ 28 String& operator= (const String& that) { 29 if (&that != this) { 30 /* 小鸟 31 delete[] m_str; 32 m_str = new char[strlen(that.m_str)+1]; 33 strcpy (m_str, that.m_str); 34 */ 35 /* 大鸟 36 char* str = 37 new char[strlen(that.m_str)+1]; 38 delete[] m_str; 39 m_str = strcpy (str, that.m_str); 40 */ 41 // 老鸟 42 String temp (that); 43 swap (m_str, temp.m_str); 44 } 45 return *this; 46 } 47 const char* c_str (void) const { 48 return m_str; 49 } 50 private: 51 char* m_str; 52 }; 53 54 int main (void) { 55 String s1 ("Hello, World !"); 56 cout << s1.c_str () << endl; 57 String s2 = s1; 58 cout << s2.c_str () << endl; 59 String s3 ("Hello, Linux !"); 60 try { 61 s1 = s3; 62 } 63 catch (exception& ex) { 64 cout << ex.what () << endl; 65 } 66 cout << s1.c_str () << endl; 67 return 0; 68 }
1 /* 2 *string练习 3 */ 4 #include <iostream> 5 #include <cstring> 6 #include <cstdio> 7 using namespace std; 8 int main (void) { 9 string s1 ("hello"); 10 string s2 = "world"; 11 (s1 += " ") += s2; 12 cout << s1 << endl; 13 string s3 = s1; 14 cout << s3 << endl; 15 cout << (s1 == s3) << endl; 16 s3[0] = 'H'; 17 cout << s3 << endl; 18 cout << (s1 > s3) << endl; 19 cout << s1.length () << endl; 20 cout << (s1 == s3) << endl; 21 cout << (strcasecmp (s1.c_str (), 22 s3.c_str ()) == 0) << endl; 23 cout << sizeof (s1) << endl; 24 FILE* fp = fopen ("string.txt", "w"); 25 // fwrite (&s3, sizeof (s3), 1, fp); 26 fwrite (s3.c_str (), sizeof (char), 27 s3.length (), fp); 28 fclose (fp); 29 return 0; 30 }
1 /* 2 * 我实现的String类 3 * 文件名:String.h 4 * */ 5 #ifndef _STRING_H__ 6 #define _STRING_H__ 7 class C_String{ 8 public: 9 C_String(void); 10 C_String(const char*); 11 C_String(const C_String&); 12 ~C_String(void); 13 C_String& operator=(const C_String &); 14 const char* c_str(void); 15 16 private: 17 unsigned mun_length; 18 char * mp_str; 19 20 }; 21 #endif //_STRING_H___
1 /* 2 * 文件名:String.cpp 3 * */ 4 #include "C_String.h" 5 #include <iostream> 6 7 using namespace std; 8 9 C_String::C_String(void) 10 { 11 mp_str = new char('\0'); 12 cout <<"构造函数C_String(void)已经执行"<< endl; 13 } 14 15 C_String::C_String(const char * str_source) 16 { 17 int i = 0; 18 for (i = 0; *(str_source + i) != '\0'; i++) 19 ; 20 mun_length = i - 1; 21 mp_str = new char[i]; 22 if (mp_str) { 23 for ( ; i >= 0; --i) 24 mp_str[i] = str_source[i]; 25 } 26 cout <<"构造函数C_String(conts char *)已经执行"<< endl; 27 } 28 C_String::~C_String(void) 29 { 30 if (mp_str) { 31 mun_length = 0; 32 delete[] mp_str; 33 cout <<"析构函数~C_String(void)" << endl; 34 } 35 } 36 37 C_String::C_String(const C_String& i_source) 38 { 39 int i = 0; 40 mun_length = i_source.mun_length; 41 mp_str = new char[mun_length + 1]; 42 if (mp_str) { 43 for (i = mun_length + 1; i >= 0; --i) 44 mp_str[i] = i_source.mp_str[i]; 45 } 46 cout <<"构造函数C_String(conts C_String&)已经执行,这是一个拷贝构造"<< endl; 47 } 48 49 C_String& C_String::operator=(const C_String &i_source) 50 { 51 int i = 0; 52 mun_length = i_source.mun_length; 53 if (&i_source != this) { 54 delete[] mp_str; 55 mp_str = new char[mun_length + 1]; 56 if (mp_str) { 57 for (i = mun_length + 1; i >= 0; --i) 58 mp_str[i] = i_source.mp_str[i]; 59 } 60 } 61 cout <<"拷贝赋值函数C_String& operator=(const C_String &)已经执行" << endl; 62 63 return *this; 64 } 65 66 const char* C_String::c_str(void) 67 { 68 return mp_str; 69 }
1 /* 2 * 文件名: main.cpp 3 * 描述:测试我的String类实现 4 * */ 5 #include <iostream> 6 #include "C_String.h" 7 8 using namespace std; 9 10 void print(const char*); 11 C_String print(C_String); 12 int main() 13 { 14 15 cout <<"构造i_string1" << endl; 16 C_String i_string1; 17 //C_String(print(i_string1));//会造成段错误,print返回的是一个匿名对象,依据该匿名对象再构造一个匿名对象会报段错误。 18 C_String i = C_String(print(i_string1));//gcc优化代码,使得print返回的匿名对象命名为i 19 C_String("pini_ig)");//对于构造的该匿名对象,会被立即析构掉 20 21 22 cout << "打印普通字符数组p_str" << endl; 23 const char *p_str = "Hello World!"; 24 print(p_str); 25 26 cout <<"用普通字符数组p_str来构造i_string2" << endl; 27 C_String i_string2 = p_str; 28 print(i_string2); 29 30 cout <<"拷贝i_string2来构造i_string3"<< endl; 31 C_String i_string3 = i_string2; 32 print(i_string3); 33 34 cout <<"拷贝i_string3来构造i_string4"<<endl; 35 C_String i_string4(i_string3); 36 print(i_string3); 37 38 cout <<"用\"LIBIN\"构造i_string5" << endl; 39 C_String i_string5("LIBIN"); 40 print(i_string5); 41 42 cout <<"i_string4 = i_string5" << endl; 43 i_string4 = i_string5; 44 print(i_string4); 45 46 47 } 48 49 C_String print(C_String i_string) 50 { 51 cout << i_string.c_str() << endl; 52 cout <<"CLASS" << endl; 53 return i_string; 54 } 55 56 void print(const char *p_str) 57 { 58 cout <<"!class"<<endl; 59 cout << p_str << endl; 60 }