先了解什么是多态,多态是指同样的消息被不同类型的对象接受时导致不同的行为,从实现角度来讲分为编译时的多态和运行时的多态,前者是在编译的过程中确定了同名操作的具体操作对象,而后者是在运行过程中才动态确定操作所针对的具体对象,这种确定操作对象的过程就是绑定,就是把一条消息和一个对象的方法相结合的过程,前者是静态绑定,后者是动态绑定,也叫晚期绑定或后绑定。只有虚函数实现的是动态绑定,其它全部是静态绑定。
protected关键字声明的成员只能由继承派生类访问,本类外无法访问。
复制构造函数具有一般构造函数的所有特性,其作用是使用一个已经存在的对象去初始化同类的一个新对象。在三种情况下被调用:一.当用类的一个对象去初始化该类的另一个对象时;二.如果函数的形参是类的对象,调用函数,进行形参和实参结合时;三.如果函数的返回值是类的对象,函数执行完成返回调用者时。默认的复制构造函数实现的是对象间元素的一一对应复制,但是当类的数据成员含有指针时,会将指针也复制过来,这使得两个对象指向同一个内存地址,表面是好像完成了复制,但是实际上待复制对象并没有产生自己真正的副本,这就是所谓的“浅复制”。“浅复制”的危害是,当我们要释放动态申请的内存时,由于两个对象的指针成员均指向同一个地址,会导致同一内存地址被释放两次,造成内存泄漏。因此我们引入“深复制”概念,即我们自己显示定义复制构造函数,确保不会出现不同指针指向同一内存地址的情况。
异常处理的目的是,允许用户排除环境错误,继续运行程序。
#include
using namespace std;
int divide(int x,int y){
if(y==0)
throw x;
return x/y;
}
int main(){
try{
cout<<"8/2="<<divide(8,2)<<endl;
cout<<"8/0="<<divide(8,0)<<endl;
}
catch(int e){
cout<<e<<"is divided by zero"<<endl;
}
cout<<"That is ok."<<endl;
return 0;
}
结果:
8/2=4
8is divided by zero
That is ok.
如果始终没有找到与被抛掷异常相匹配的catch子句,最终main函数会结束执行。
#include
using namespace std;
int b=6;
int f1(int &x){
cout<<b<<endl;//6
b=b+2;
x=x+3;
cout<<x<<endl;//5
return b;
}
int main(){
int a=2,b=2;
b+=f1(a);
cout<<b<<endl;//10
cout<<a<<endl;//5
cout<<::b;//8,::是全局作用域限定符
return 0;
}
#include
#include
using namespace std;
ofstream out("hm.out");
class HM{
static int oCount;
public:
HM(){oCount++;}
static void print(const string& msg=""){
if(msg.size()!=0)
out<<msg<<":";//out对象是我们自己定义的,不是cout
out<<"oCount="<<oCount<<endl;
}
~HM(){
oCount--;
print("~HM()");
}
};
int HM::oCount=0;
HM f(HM x){
x.print("x in f()");
return x;
}
int main(){
HM h;
HM::print("test1");
HM h2=f(h);//此处是本题最复杂的地方,形参和实参的结合会执行一次复制构造函数,然后f(n)返回时会赋值给h2,此处涉及到第二次复制构造函数的执行。因为本题使用默认的复制构造函数,没有oCount++操作,f(n)执行完毕后,内部的x对象会被析构,
HM::print("test2");执行析构函数。
return 0;
}//程序执行完毕,h和h2两个对象会被析构,执行两次析构函数。
运行结果:
test1:oCount=1
x in f():oCount=1
~HM():oCount=0
test2:oCount=0
~HM():oCount=-1
~HM():oCount=-2
#include
using namespace std;
class pet{
int i;
public:
virtual void eat()const{
cout<<"pet:eat"<<endl;
}
void speak() const{
cout<<"pet:speak"<<endl;
}
virtual void sleep(){
cout<<"pet:sleep"<<endl;
}
};
class goldfish:public pet{
public:
void eat() const{
cout<<"goldfish:eat"<<endl;
}
virtual void speak() const{
cout<<"goldfish:speak"<<endl;
}
};
int main(int argc,char* argv[]){
cout<<"sizeof:pet"<<sizeof(pet)<<endl;//8B
goldfish bob;
cout<<"sizeof:bob"<<sizeof(bob)<<endl;//8B
bob.eat();//"goldfish:eat"
bob.speak();//"goldfish:speak"
bob.sleep();//"pet:sleep"
pet *p=&bob;
p->eat();//"goldfish:eat"
p->speak();//"pet:speak"
p->sleep();//"pet:sleep"
return 0;
}
class X{
int a;
public:int func(void){
return a++;
}
};
class Y:public X{
public:void set(int c){
this->a=c;//a是类X的私有数据成员,类Y是无法访问的,所以这里错了
}
int describe() const{//应该去掉const,因为常成员函数不能更新目的对象的数据成员
return func();
}
};
#include
#include
using namespace std;
template<class T>
class Array{
enum{size=100};
T A[size];
public:
};
void main(){
Array<string> as;
as[0]="0";
for(int i=1;i<as.size();i++){
as[i]+=as[i-1];
}
}
修改后:
#include
#include
#include
using namespace std;
template<class T>
class Array{
private:
enum{size=100};
T A[size];
public:
int getsize() const;
T &operator [](int i);
};
template <class T>
int Array<T>::getsize() const{
return size;
}
template <class T>
T &Array<T>::operator [](int i){
assert(i>=0&&i<size);
return A[i];//返回数组Array[i]的引用
}
void main(){
Array<string> as;
as[0]="0";
int i;
for(i=1;i<as.getsize();i++){
as[i]+=as[i-1];
}
for(i=0;i<as.getsize();i++){
cout<<as[i]<<endl;
}
}
#include
#include
#include
using namespace std;
const int MaxStackSize=50;
template<class T>
class Stack{
private:
T list[MaxStackSize];
int top;
public:
Stack();
void Push(const T &item);
T Pop();
void Clear();
const T &Peek() const;
bool isEmpty() const;
bool isFull() const;
};
template<class T>
Stack<T>::Stack():top(-1){}
template<class T>
void Stack<T>::Push(const T &item){
assert(!isFull());
list[++top]=item;
}
template<class T>
T Stack<T>::Pop(){
assert(!isEmpty());
return list[top--];
}
template<class T>
void Stack<T>::Clear(){
top=-1;
}
template<class T>
const T &Stack<T>::Peek() const{
assert(!isEmpty());
return list[top];
}
template<class T>
bool Stack<T>::isEmpty() const{
return top == -1;
}
template<class T>
bool Stack<T>::isFull() const{
return top==MaxStackSize - 1;
}
int main(){
Stack<int> s1;
int i;
for (i=0;i<MaxStackSize;i++){
s1.Push(i);
}
cout<<"after pushed 50 times the top is:"<<s1.Peek()<<endl;
for(i=0;i<MaxStackSize-1;i++){
s1.Pop();
}
cout<<"after poped 49 times the top is:"<<s1.Peek()<<endl;
s1.Pop();
cout<<"isEmptt:"<<s1.isEmpty()<<endl;
system("pause");
return 0;
}
#include
void swap(int &a,int &b){
a=a+b;
b=a-b;
a=a-b;
}
void main(){
int a=19,b=15;
cout<<"a="<<a<<",b="<<b<<endl;
swap(&a,&b);//这里错了,应该是swap(a,b)
cout<<"a="<<a<<",b="<<b<<endl;
}
刚刚看了一遍清华的C++引用课程,有几个点:
(1)引用必须在定义时直接初始化,不能先定义之后再初始化,引用是不可更改的。
(2)函数在调用时候才会给形参分配内存空间,也就是调用之前函数内的局部变量不存在。
综合这两个点知道,在函数调用时才会给形参分配存储空间,那么当函数调用时,这个引用t才被创建,然后立即用实参对其进行初始化,也就是上面写的&t = a;这个操作。也就是在形实结合的时候,这个引用才被定义。
#include
#include
using namespace std;
template <class T>
void Swap(T &a,T &b){
T temp;
temp=a;
a=b;
b=temp;
}
void main(){
int a=5,b=9;
//char s1[]="hello",s2[]="hi";这样调用Swap(s1,s2)交换的是地址,所以错了。
char *s1="hello",*s2="hi";//这样调用Swap(s1,s2)交换的
Swap(a,b);
Swap(s1,s2);
cout<<"a="<<a<<",b="<<b<<endl;
cout<<"s1="<<s1<<",s2="<<s2<<endl;
}