一、类
类的静态变量
1、在类内进行声明
2、在类外进行初始化3、只有const static int类型才能在类内进行初始化错误初始化1:
#includeclass TestClass1{public: static int s;};void main(){ TestClass1::s = 1;//error LNK1120: 1 个无法解析的外部命令;不能在此初始化}
错误初始化2(类内的初始化):
#includeclass TestClass1{public: //错误的类内初始化 static int s = 1;//error C2864: “TestClass1::s”: 只有静态常量整型数据成员才可以在类中初始化 //正确:只有const static int类型才能在类内进行初始化 //static const int s = 1; //错误:必须为整型 //static const float f = 1.0F;};void main(){ printf("static int s = %d\n", TestClass1::s);}
正确的初始化:
#includeclass TestClass1{public: static int s; const static int i; const static int a = 3; //const int b = 4;//错误的初始化,const也不可以};int TestClass1::s = 1;//int不可省略const int TestClass1::i = 2;//const int不可省略void main(){ printf("static int s = %d\n", TestClass1::s); printf("static int i = %d\n", TestClass1::i); printf("static int a = %d\n", TestClass1::a);}
复制构造函数
在C++中已经默认约定了,类对象可以复制。类对象的复制就是其中各个成员的复制。如果某个类所需要的不是这种默认方式,比如类成员里有一个指针,如果你按照默认方式去复制对象,那么就会造成两个对象共用一块内存,对一个对象的操作就会影响另一个对象,这显然不是我们所想的。那么你可以定义一个复制构造函数。如,下面这段代码:
class A{public:A(const A& ){}; //复制构造函数};
赋值操作符
class A{public:A(const A& ){}; //复制构造函数A& operator=(const A&){}//赋值函数};
A a;A b;A c = a; //调用的是拷贝构造函数c = b; //调用的是赋值函数
当然,如果你不希望类被拷贝和赋值,那么你可以把拷贝函数和赋值函数设为private,这样对象就不能被拷贝和赋值了
例子:
1 #include2 3 class TestClass2 4 { 5 int a; 6 7 public: 8 TestClass2() 9 {10 printf("call TestClass2 default construction\n");11 a = 1;12 }13 14 TestClass2(const TestClass2& right)15 {16 printf("call TestClass2 copy construction\n");17 a = right.a;18 }19 20 TestClass2& operator=(const TestClass2& right)21 {22 printf("call TestClass2 operator=\n");23 a = right.a;24 return *this;25 }26 };27 28 class TestClass329 {30 TestClass2 tc2;31 32 public:33 TestClass3(){}34 TestClass3(const TestClass2& c2)// : tc2(c2)//当有一个构造函数时,默认构造函数如果没有提供,则默认是私有的35 {36 tc2 = c2;37 }38 };39 40 void main()41 {42 //TestClass3 tc;//printf("call TestClass2 default construction\n");43 printf("TestClass2初始化\n");44 TestClass2 tc2;45 printf("TestClass3初始化\n");46 TestClass3 tc3(tc2);47 }
运行结果:
TestClass2初始化
call TestClass2 default constructionTestClass3初始化call TestClass2 default constructioncall TestClass2 operator=注意:为什么使用初始化列表效率要更高,即TestClass2(tc2)
注释掉36,将34注释去掉,使用初始化列表,则运行结果:
TestClass2初始化call TestClass2 default constructionTestClass3初始化call TestClass2 copy constructionoperator+操作符
1 #include2 using namespace std; 3 4 class Sum 5 { 6 public: 7 Sum(int val = 0); 8 Sum& operator+(Sum& other); 9 10 int Get()const11 {12 return m_val;13 }14 15 private:16 int m_val;17 };18 19 Sum::Sum(int val) : m_val(val)20 {21 }22 23 Sum& Sum::operator+(Sum& other)24 {25 this->m_val += other.m_val;26 return *this;27 }28 29 void main()30 {31 Sum a(3);32 Sum b(4);33 Sum c(5);34 Sum d = a + b + c;35 36 cout << d.Get() << endl;37 }
operator<<和友元
有的时候,你需要访问一个类的私有成员,那该怎么办呢,要么这个类公开了一个函数接口,要么我们可以使用友元函数,可以直接的访问类的私有成员,以及其它成员。有的时候,我们把一个类作为另一个类的友元时,此时叫这个类为友元类,那么这个友元类就可以访问另一个类里的所有成员
注意:友元的引入,提高了数据的共享性,加强了函数与函数之间,类与类之间的相互联系,大大提高程序的效率,这是友元的优点,但友元也破坏了数据隐藏和数据封装导致程序的可维护性变差,给程序的重用和扩充埋下了深深的隐患,这是友元的缺点.
1 #include2 using namespace std; 3 4 class UipString 5 { 6 friend std::ostream& operator<<(std::ostream&os, const UipString& obj); 7 8 public: 9 UipString(const char *str = "");//构造函数10 ~UipString();//析构函数11 UipString(const UipString& other);//拷贝构造函数12 UipString& operator=(const UipString &other);//赋值构造函数13 14 private:15 char *m_data;16 };17 18 UipString::UipString(const char *str)19 {20 if (str == NULL)21 {22 m_data = new char[1];23 *m_data = '\0';24 }25 else26 {27 m_data = new char[strlen(str)+1];28 strcpy_s(m_data, strlen(str)+1,str);29 }30 }31 32 UipString::~UipString()33 {34 if (m_data)35 {36 delete [] m_data;37 m_data = NULL;38 }39 }40 41 UipString::UipString(const UipString &other)42 {43 m_data = new char[strlen(other.m_data)+1];44 strcpy(m_data, other.m_data);45 }46 47 UipString& UipString::operator=(const UipString &other)48 {49 //More effective c++中,不能给自己赋值50 if (this == &other)51 {52 return *this;53 }54 55 char *pTemp = new char[strlen(other.m_data)+1];56 strcpy(pTemp, other.m_data);57 delete []m_data;58 m_data = pTemp;59 }60 61 ///重点////62 std::ostream& operator<<(std::ostream &os, const UipString &obj)63 {64 //因为是友元函数,可以调用obj的私有成员变量65 os << obj.m_data <
二、继承
类的继承访问特性
is-a关系只有public继承才成立。对于private继承还又是另外一回事了。C++中除了有public继承外,还有private继承和protected继承。在private继承中,派生类只能以私有方式继承基类的公有成员和保护成员,因此,基类的公有成员和保护成员在派生类中成为私有成员。另外,基类的私有成员派生类仍不能被问。
如下代码,20行可以访问,21行不可访问。Child: private Base表示,Child从Base继承的所有成员的访问权限都变为pivate,即外部不能访问Child的基类成员,因为他们的权限都是private
1 #include2 using namespace std; 3 4 class Base 5 { 6 public: 7 int public_a; 8 protected: 9 int protected_b;10 private:11 int private_c;12 };13 14 class Child: private Base15 {16 public:17 Child()18 {19 public_a = 1;20 protected_b = 2; //此处可以访问基类的protected成员21 }22 };23 24 void main()25 {26 Child child;27 //int a = child.public_a; //此处不可以访问28 }
构造与析构
看点:27行代码怎么初始化基类成员
1 #include2 using namespace std; 3 4 class Base 5 { 6 public: 7 Base(int a):a(a) 8 { 9 cout<< "Base constructor\n";10 }11 ~Base()12 {13 cout<< "Base destructor\n";14 }15 16 int GetValue() const17 {18 return a;19 }20 private:21 int a;22 };23 24 class Derived: public Base25 {26 public:27 Derived(int a, int b): b(b), Base(a)//注意:调用基类构造函数28 {29 cout << "Derived constructor\n";30 }31 ~Derived()32 {33 cout << "Derived destructor\n";34 }35 36 int Get()37 {38 return b + GetValue();39 }40 private:41 int b;42 };43 44 void main()45 {46 Derived b(2, 3);47 int a = b.Get();48 }
运行结果:
Base constructor
Derived constructorDerived destructorBase destructor从运行结果我们可以看出,构造函数是先执行基类的构造然后才是派生类的构造;而析构则是相反的顺序。从代码中我们可以看到,如果基类有数据需要初始化,我们是在派生类里把数据传过去供基类初始化。这种方式称为显示初始化。
多重继承
1 #include2 using namespace std; 3 4 class A 5 { 6 public: 7 int a; 8 }; 9 10 class B: /*virtual*/ public A11 {12 public:13 int b;14 };15 16 class C: /*virtual*/ public A17 {18 public:19 int c;20 };21 22 class D: /*virtual*/ public B, /*virtual*/ public C23 {24 public:25 int d;26 };27 28 void main()29 {30 D cd;31 //cd.a = 100;//error C2385: 对“a”的访问不明确32 cd.B::a = 1000;33 cout << "cd.B::a=" << cd.B::a << endl;34 }
发现31行cd.a = 1000;这句编译不过,为什么呢,因为这里出现了歧义,编译器不知道这个a是从B类继承下来的a,还是从C类继承下来的,当然我们可以在a的前面加上::作用域限定符,指定是哪个类的a,如cd.B::a = 1000;这种作用域限定符有它的特点:
1)浪费了存储空间,保存了多个相同的副本2)在访问基类的成员时,要求指明访问路径。解决方法是使用虚基类,将10,16,22行“virtual”注释去掉。采用虚基类方式定义派生类,在创建派生类的对象时,类层次结构中虚基类的成员只出现一次,即基类的一个副本被所有派生类对象所共享。使用虚基类派生方式你可以节约内存空间。再就是避免在多重派生类中类成员的不明确性。
虚函数
如果想通过基类指针调用派生类中覆盖的成员函数,那么只有使用虚函数。
1 #include2 using namespace std; 3 4 class Base 5 { 6 public: 7 Base() 8 { 9 cout << "Base constructor\n";10 }11 12 /*virtual*/ ~Base()13 {14 cout << "Base destructor\n";15 }16 17 virtual void Display() const18 {19 cout <<"Base::display()\n";20 }21 };22 23 class Derived : public Base24 {25 public:26 Derived()27 {28 cout << "Derived constructor\n";29 }30 31 /*virtual*/ ~Derived()32 {33 cout << "Derived destructor\n";34 }35 36 virtual void Display() const37 {38 cout <<"Derived::display()\n";39 }40 };41 42 int main()43 {44 Base* pb = new Derived();45 pb->Display();46 if (pb)47 {48 delete pb;49 pb = NULL;50 }51 return 0;52 }
虚析构函数
如上例子运行结果:
Base constructor
Derived constructorDerived::display()Base destructor将12、31行的“virtual”关键字注释去掉,
运行结果:
Base constructor
Derived constructorDerived::display()Derived destructorBase destructor