1.使用类或者结构体时,再定义的后面要添加";"
class Flyingtime
{
......
};
struct Flyingtime
{
.......
};
2.在windows下使用include包含相对路径下的文件要使用"/"
例如在flyingtime下有如下文件夹:
header、source、information等
如果要在source文件夹下的test.cpp要用到header下的test.h文件
#include "../header/test.h"
3. 在类中声明"static"类型变量,在类的定义中就不能再次使用"static"了
- //test.h
- #ifndef TEST_H
- #define TEST_H
- class test
- {
- public:
- test();
- ~test();
- static int const test_int() const;
- private:
- static int int_arg;
- };
- #endif
- //test.cpp
- #include <iostream>
- using std::cout;
- using std::endl;
- #include "test.h"
- int test::int_arg=0;
- test::test()
- {
- int_arg++;
- cout << "test class start!" << endl;
- }
- test::~test()
- {
- int_arg--;
- cout << "test class end!" << endl;
- }
- int const test::test_int() const
- {
- cout << "static argument is : " << int_arg << endl;
- return int_arg;
- }
4.类没有对象也可以访问其静态成员变量,但必须用静态成员函数进行访问
- //承上代码
- #include <iostream>
- using std::cout;
- using std::endl;
- #include "test.h"
- void main()
- {
- test *abc= new test();
- abc->test_int(); //此时可以用abc->int_arg来获取静态变量的值,因为test类的对象abc存在
- delete abc;
- abc=0;
- test::test_int(); //此时必须用类的静态成员函数来获取静态变量的值
- }
5. 在一些信息安全较高的地方最好使用“代理类”
假设一个类进行信息加密Encrypt,他的一个代理类EncryptPtr
encrypt.h
- #ifndef ENCRYPT_H
- #define ENCRYPT_H
- #region
- class Encrypt
- {
- public:
- Encrypt(FILE *,FILE *);
- ~Encrypt();
- void Encrypt_Code();
- private:
- int m_Encrypt; //这里有构造函数随即生成
- };
- #endregion
- #endif
encrypt.cpp
- //encrypt.cpp
- #include <iostream>
- using std::cout;
- using std::endl;
- #include <ctime>
- #include <cstdlib>
- #include "encrypt.h"
- Encrypt::Encrypt(FILE *fpinput,FILE *fpoutput)
- {
- assert(fpinput); //以前没用过,这里也不知道是不是正确
- assert(fpoutput);
- srand(time(0));
- m_Encrypt=rand(); //生成加密时用的密钥
- }
- Encrypt::~Encrypt()
- {
- m_Encrypt=0;
- }
- void Encrypt::Encrypt_Code()
- {
- //read fpinput->buffer
- //buffer xor m_Encrypt
- //buffer write->fpoutput
- }
encrypt_ptr.h
- #ifndef ENCRYPT_PTR_H
- #define ENCRYPT_PTR_H
- class Encrypt; //在这里只需要声明一次,而不需要包含它的头文件
- class Encrypt_Ptr
- {
- public:
- Encrypt_Ptr(FILE *,FILE *);
- ~Encrypt_Ptr();
- privat:
- Encrypt_Ptr *cpEncrypt;
- };
- #endif
encrypt_ptr.cpp
- #include "encrypt.h" //被代理类的头文件
- #include "encrypt_ptr.h" //代理类的头文件
- Encrypt_Ptr::Encrypt_Ptr(FILE *fpinput,FILE *fpoutput)
- :cpEncrypt(new Encrypt(fpinput,fpoutput))
- {
- cpEncrypt->Encrypt_Code();
- }
- Encrypt_Ptr::~Encrypt_Ptr()
- {
- delete cpEncrypt;
- }
7.静态成员函数只能访问静态成员变量,将静态成员函数声明为"const"是语法错误!
因为静态成员函数是独立于类而存在的,也就是说类并不给其静态成员函数传递this指针,这就意味着静态成员函数只能看到静态成员变量,而类中的其他成员变量它是看不到的。成员函数的const实际上也是修饰的this指针,所以将静态成员函数声明为"const"是语法错误!
8.可以重载的操作符有:
+ - * / % ^ & | ~ ! = > < += -= *= /= %= ^= &= |= << >>
>>= <<= == != <= >= && || ++ -- ->* ' -> [] () new delete new[] delete[]
不能被重载的操作符有:
. .* :: ?: sizeof
9.什么时候定义类成员操作符重载,什么时候定义非类成员操作符重载?
(1)如果一个重载操作符是类成员,那么只有当跟它一起使用的左操作数是该类对象时,它才会被调用,如果该操作符的左操作数必须是其他类型,那么重载操作符必须是非类成员操作符重载。
(2)C++要求,赋值(=),下标([ ]),调用(())和成员访问箭头(->)操作符必须被指定为类成员操作符,否则错误。
10.当做重载操作符操作时,如果类的实例对象出现在重载操作符的右边时,最好将该重载操作符的函数声明为友元函数
- //array.h
- #ifndef ARRAY_H
- #define ARRAY_H
- #include <iostream>
- using::ostream;
- using::istream;
- class Array
- {
- public:
- Array(int=10);
- Array(const Array &);
- friend ostream &operator <<(ostream &,const Array &); //重载 <<
- friend istream &operator >>(istream &,Array &); //重载 >>
- Array operator =(const Array &); //重载 =
- bool operator ==(const Array &) const; //重载 ==
- bool operator !=(const Array &) const; //重载 !=
- int operator [](int subscript); //重载 []
- // 与上一个效果应该是一样的
- // int operator [](int subscript);
- int GetSize();
- ~Array();
- private:
- int m_size;
- int *lp_array;
- static int m_obj_counter;
- };
- #endif
- //array.cpp 这里是array类的实现
- #include <iostream>
- using std::cin;
- using std::cout;
- #include <iomanip>
- using std::setw;
- #include <cstdlib>
- #include <cassert>
- #include "array.h"
- Array::Array(int size)
- {
- m_size=size>0 ? size : 10;
- l p_array=new int[m_size];
- assert(lp_array!=0);
- }
- Array::Array(Array &rf_array)
- :m_size=rf_array.m_size
- {
- // m_size=rf_array.m_size; //与前面的一样
- lp_array=new int[m_size];
- for(int i=0;i<m_size;i++)
- {
- lp_array[i]=rf_array.lp_array[i];
- }
- }
- Array::~Array()
- {
- delete[] lp_array;
- m_size=0;
- }
- ostream &operator <<(ostream &output,const Array &rf_array)
- {
- for(int i=0;i<rf_array.m_size;i++)
- {
- output << rf_array.p_array[i] << setw(10);
- if(rf_array.m_size%5==0)
- output << endl ;
- }
- if(rf_array.m_size%5!=0) output << endl;
- return output;
- }
- istream &operator >>(istream &input,Array &rf_array)
- {
- for(int i=0;i<rf_array.m_size;i++)
- {
- input >> rf_array.lp_array[i] ;
- }
- return input;
- }
- Array &Array::operator=(const Array &rf_array)
- {
- if(&rf_array!=this)
- {
- if(m_size!=rf_array.m_size) m_size=rf_array.m_size;
- delete[] lp_array;
- lp_array=new int[m_size];
- assert(lp_array!=0);
- for(int i=0;i<m_size;i++)
- {
- lp_array[i]=rf_array.lp_array[i];
- }
- }
- return *this;
- }
- int &Array::operator[](int subscript)
- {
- assert(subscript>=0&&subscript<m_size);
- return lp_array[subscript];
- //在实际程序中验证该返回值是正确的。。。 cout << arrayobject[intobject] or
- //arrayobject[intobject]=intobject 都是正确的
- //在C++编程金典中有个例子它返回值类似于下面:
- // return lp_array[subscript];
- //我觉得既然是返回值为"&"就不应该返回一个数组的值,而是当前数值下标的指针
- //这里我觉得有些疑问,如果有朋友知道该返回什么请告诉我一声,谢谢哈
- }
- bool Array::operator ==(const Array &rf_array) const
- {
- if(rf_array.m_size!=m_size) return false;
- for(int i=0;i<m_size;i++)
- {
- if(lp_array[i]!=rf_array.lp_array[i]) return false;
- }
- return true;
- }
- bool Array::operator !=(const Array &rf_array) const
- {
- if(rf_array.m_size!=m_size) return true;
- for(int i=0;i<m_size;i++)
- {
- if(lp_array[i]!=rf_array.lp_array[i]) return true;
- }
- return false;
- }
- //main.cpp
- #include <iostream>
- using std::cout;
- using std::cin;
- #include "array.h"
- bool main()
- {
- Array a(8),b(6);
- //因为类实例对象出现在重载操作符的右边,所以在定义重载操作的时候需要定义为
- //friend类型,编译器遇到表达式cin >> arrayobject;会解析成
- //operator >>(cin,arrayobject)如果定义为类的成员函数时可能写的时候
- //就不是那么好理解了(如果为类的成员函数则需写成arrayobject<<cout)
- cin >> a ;
- cin >> b ;
- cout << a ;
- cout << b;
- if(a!=b) a=b;
- cout << a;
- cout << b;
- if(a==b) return true;
- return false;
- }
比较有疑问的那部分代码的反汇编代码如下:
return p_array[subscript];
004130C9 mov eax,dword ptr [this]
004130CC mov ecx,dword ptr [eax+4]
004130CF mov edx,dword ptr [subscript]
004130D2 lea eax,[ecx+edx*4]
可以看出这里 return p_array[subscript]就是返回它的指针
11.派生类的对象就是基类对象,但反过来则不成立;如果想让基类对象转换成派生类对象必须进行显式的转换
如果将上面的:
template<class T1>
template<class T2>
写成:
template <class T1,class T2>即为错误,因为它们两个的作用域是完全不同的
- //基类
- class a
- {
- public:
- a();
- ~a();
- protected:
- int x;
- int y;
- };
- a::a()
- :x(0),y(0)
- {}
- a::~a()
- {}
- //通过继承基类a生成派生类b
- class b : public a
- {
- public:
- b();
- ~b();
- private:
- int count;
- };
- b::b()
- :a() //这里显式初始化基类a
- {}
- b::~b()
- {}
- int main()
- {
- a *cp_a=0,a_obj;
- b *cp_b=0,b_obj;
- cp_a=&a_obj; //同类型赋值
- cp_b=&b_obj;
- cp_a=&b_ojb; //派生类的对象就是基类的对象体现在这里
- cp_b=static_cast<b *>&a_obj; //将基类对象转换成派生类对象需要
- cp_b=static_cast<b *>cp_a; //显式的转换
- //这里需要注意使用强制转换需要程序员自己对该强制转换负责,即程序员要控制指针的使用
- //当指针指向一个不存在的成员变量时也许不会出错,但是用指针指向一个不存在的成员函数
- //时一定会出错!
- return 0;
- }
12.基类的构造函数跟赋值操作符不能被派生类函数继承,但派生类的构造函数和赋值操作符可以调用基类的构造函数和赋值操作符。
13.关于位段的一些问题:
- //使用关键字struct来声明位段
- struct test
- {
- unsigned a : 2 ;
- unsigned b : 4 ;
- unsigned c : 2 ;
- };
(1.)位段操作与机器有关,有些机器允许位段跨越字边界,而有的不可以;
(2.)位段成员的类型只能是unsigned或者int;
(3.)试图取位段的地址或者试图访问位段中单独的位都是错误的操作;
14.在迭代器中添加新的元素可能会使迭代器失效,尤其存储end返回的迭代器。
因为end返回的迭代器并非是该容器的最后一个迭代器,而是该容器的最后一个迭代器的下一个
- #include <iostream>
- #include <vector>
- int main()
- {
- std::vector<int> v;
- std::vector<int>::iterator first=v.begin(),
- last=v.end();//这里导致last迭代器失效
- while(first != last) //因为前面last失效,所以这里也是不对的
- //最终可能导致一个死循环
- {
- first = v.insert(first,66);
- ++first;
- }
- }
15.应该保持成员初始化列表中成员列出的顺序和它们在类中声明的顺序相同,因为类成员是按照它们在类里声明的顺序进行初始化的,和它们在成员列表中列出的顺序没有一点关系。
例如:
- #ifndef ARRAY_H
- #define ARRAY_H
- #include <cstdlib>
- #include <vector>
- template<typename T>
- class Array
- {
- public:
- Array(int lowBound,int highBound);
- private:
- vector<T> data;
- size_t size;
- int mLow;
- int mHigh;
- };
- template <typename T>
- Array<T>::Array(int lowBound,int highBound)
- :mLow(lowBound),mHigh(highBound),size(highBound-lowBound+1),data(size)
- //注意上面在成员初始化列表中列出的成员初始化顺序并不代表真实的初始化顺序
- //真正的初始化话顺序在类的声明中决定了,之所以要让成员初始化列表中的顺序同
- //类声明的顺序一致是想提醒用户类的成员将按照什么样的顺序进行初始化
- {}
- #endif
这里data的大小并不确定,因为首先初始话的是vector<T> data(size),而此时的size并没有被初始化。正确的例子如下:
- #ifndef ARRAY_H
- #define ARRAY_H
- #include <cstdlib>
- #include <vector>
- template<typename T>
- class Array
- {
- public:
- Array(int lowBound,int highBound);
- private:
- int mLow; //类成员的声明顺序被改变了
- int mHigh;
- size_t size;
- vector<T> data;
- };
- template <typename T>
- Array<T>::Array(int lowBound,int highBound)
- :mLow(lowBound),mHigh(highBound),size(highBound-lowBound+1),data(size)
- //成员初始化列表与类声明中的保持一致了
- //最终data会被初始化成一个size大小的vector<T>类型的变量
- {}
- #endif
16.注意赋值运算以及拷贝构造函数,尤其是存在继承的关系中
- template<typename T>
- class Base
- {
- public:
- Base(const Base& default)
- :mData(default.mData)
- {}
- Base& operator = (Base& default)
- //注意这里的返回值类型,必须为*this类型,这样可以保持赋值操作的连续性
- //如:Base<int> a = b = c = d = 5;
- //同时返回值不能为const类型
- //即const Base& operator = (Bast<T>&) Error!
- //如果用户这样赋值Base<int> (a=b)=c=5;
- //因为(a = b)返回为const类型,所以再用c给它赋值就会出现错误
- {
- if(this == & default)
- //这里检查是否对自身赋值
- return *this;
- mData = default.mData;
- return *this;
- }
- private:
- T mData;
- };
- template <typename T>
- class Child : public Base<T>
- {
- public:
- Child(const Child& default)
- :Base(default),mSubData(default.mSubData)
- //如果在该拷贝构造函数中不加入Bast(default)那么基类中的
- //数据成员就不能得到初始化
- //上面成员列表的顺序也是按照它们初始化的顺序书写的
- //保持一个良好的编程习惯不容易啊!
- {
- Child& operator=(const Child& default)
- {
- if(this == &default)
- //检查是否是对自身赋值
- return *this;
- //Base<T>::operator=(default);
- //上面被注释的部分有的编译器并不支持
- static_cast<Base&>(*this) = default;
- mSubData=default.mSubData;
- return *this;
- }
- private:
- T mSubData;
- }
17.如果派生类中私有继承基类,并且在派生类中在调用基类中的函数可以用using显式的声明。
- #include <iostream>
- using std::cout;
- using std::endl;
- using std::cin;
- class a
- {
- public:
- void showa()
- {
- cout << "a.show/n";
- }
- };
- class b : private a
- {
- public:
- using a::showa; //这里只能用名称,而不能用showa()
- void showb()
- {
- cout << "b.show/n";
- showa();
- }
- };
- void main()
- {
- b name;
- name.showb();
- int i;
- cin >> i;
- return;
- }
18.请看上面的例子,基类a并没有实现virtual ~a(),为了让以a为基类的派生类不产生错误,派生类只能实现私有继承。之所以这么做有以下原因:
(1)不实现virtual ~a(),是为了不再增加派生类的体积,如果实现virtual ~a(),将引入vtab。
(2)之所以在派生类中私有继承基类,是为了在派生类中不能直接调用基类的析构函数,如:
a *pa;
b *pb=new b;
pa=pb; //加入基类的私有继承这里就会出现错误
delete pa; //这里不加入virtual ~a()将会出现错误,我们把a私有继承,那么这里就不会出现错误!因为a的析构函数是私有的。
19.从一个类模板生成的每个类都将得到类模板中每个static成员的一个副本。
摘自《C++程序设计(特别版)》,还没有完全明白它的意思,有一个例子:
20.一个模板的模板参数表与其模板成员的模板参数表不能组合在一起。