全部博文(842)
分类: C/C++
2012-05-30 14:14:53
(1)OO的基本概念:类、对象、继承。
(2)C++中的空类,默认产生哪些成员函数?
A: 默认构造函数、拷贝构造函数、析构函数和赋值函数。
10.2 类和结构(1)c++中struct 和class 除了默认访问控制不一样以外,其他都一样:
Class 默认访问控制为private, struct 默认访问控制为public.
(2)
struct Test
{
Test(int){}
Test(){}
void fun(){cout<<"func"<
};
int main()
{
Test a(1);
Test b(); //此处是一个函数声明,返回值为Test类型,而不是对象定义!
Test b1; //对象定义
Test *c = new Test();
Test *d = new Test(2);
a.fun();
b.fun(); //error!
c->fun();
d->fun();
return 0;
}
10.3 成员变量(1)静态成员函数
class Cat
{
public:
Cat(int age):itsAge(age){HowManyCats++;}
virtual ~Cat(){HowManyCats--;}
virtual int getAge(){return itsAge;}
virtual void setAge(int age){itsAge=age;};
static int HowManyCats;
private:
int itsAge;
};
// int Cat::HowManyCats=0; //对静态成员数据如此赋值
int main()
{
const int maxCats =5;
int i;
Cat *catHouse[maxCats];
for(i=0; i
catHouse[i]=new Cat(i);
for(i=0; i<=maxCats; i++)
{
cout<<"there are "<
if(!Cat::HowManyCats) break;
cout<<"delete the cat which is "<
delete catHouse[i];
catHouse[i] = 0;
}
return 0;
}
上面程序的错误是定义了静态数据成员,但没有为之赋值!
对于私有静态数据成员:private: static int HowManyCats;
可以用公有的静态成员函数访问:public: static int GetHowMany(){return HowManyCats;},也可以用非静态成员函数访问,用对象调用此成员函数。
(2)初始化列表的成员变量的初始化
class Base
{
private:
int a;
int b;
public:
Base(int i):b(i),a(b){}
Base():b(0),a(b){}
int getA(){return a;}
int getB(){return b;}
};
int main()
{
Base base(90);
cout<
return 0;
}
初始化列表的成员变量的初始化是根据成员变量的声明顺序初始化的。
此时输出的第一个数是一个随机数,第二个是90
该一下声明的顺序,或者初始化的顺序都可以得到90 90.
(3)常量成员函数
class Base { const int size=9; };//'size' : must be initialized in constructor base/member initializer list
改成如下:class Base {public: Base(){const int size=9;} };
或者:class Base {public: Base():size(9){} const int size;};
10.4 构造函数和析构函数(1)MFC类库中,CObject的析构函数是虚拟的,为什么要这样定义?
A:
class Base
{
public:
Base(){cout<<"base build\n";}
~Base(){cout<<"base delete\n";}
};
class Child: public Base
{
public:
Child(){cout<<"child build\n";}
~Child(){cout<<"child delete\n";}
};
int main()
{
Child c;
return 0;
}
子类对象的创建时,先调用父类的构造函数然后调用自己的构造函数,析构时先调用自己析构函数后调用父类的析构函数。当main函数中有如下代码时:Base *pBase; Child c; pBase = &c;
当delete pBase; 时,调用的是父类的构造函数,此时析构c时,无法再调用子类的析构函数, 造成错误。而将base析构函数定义为virtual时,pBase被撤销时会先调用Child的析构函数再调用Base的析构函数。
上面的例子所有对象都存在栈中,当离开作用域时对象会被自动撤销,不会有什么问题。但当Child类的构造函数在堆中分配了内存,析构函数又不是virtual时,撤销pBase时,不会调用Child的析构函数,不会释放Child对象占据的内存,从而造成内存泄露。
当CObject的析构函数设为virtual时,其派生类的析构函数都将自动变为virtual型,从而不会出现由于析构函数未被调用而导致的内存泄露。
(2)构造函数和析构函数都可以是内联函数。
(3)
class B
{
private:
int data;
public:
B(){cout<<"default constructor!e\n";};
B(int i):data(i){cout<<"constructed by parameter "<
~B(){cout<<"destructed"<
};
B play(B b){return b;}
int main()
{
B tmp = play(5);//5通过隐式类型转换调用了B::B(int i),将5转换为B类型
return 0;
}
输出: constructed by parameter 5
destructed //play(5)返回时,参数的析构函数被调用
destructed//tmp析构函数的调用,tmp构造函数的调用是编译器生成的默认拷贝构造函数
10.5 拷贝构造函数和赋值函数(1)编写string类的构造函数,析构函数和赋值函数。
class String
{
public:
String(const char *str = NULL);//构造函数
String(const String &other); //拷贝构造函数
~String(void);
String & operator= (const String & other);
private:
char *m_data;
};
String::~String()
{
if(m_data)
//delete[] m_data;//也可以
delete m_data;
}
String::String(const char *str)
{
if(str==NULL)
{
m_data = new char[1];
if(m_data)
*m_data = '\0';
}
else
{
int length = strlen(str);
m_data = new char[length+1];
if(m_data)
strcpy(m_data, str);
}
}
String::String(const String &other)
{
int length = strlen(other.m_data);
m_data = new char[length+1];
if(m_data)
strcpy(m_data, other.m_data);
}
String & String::operator= (const String & other)
{
if(this == &other)
return *this;
delete[] m_data;//忘记释放的话,造成内存泄露
int length = strlen(other.m_data);
m_data = new char[length+1];
strcpy(m_data, other.m_data);
return *this;
}
String & String::operator= (const String & other)
其中const的两个作用:
A 如果没有const的话,对于:String s1("pello"); const String s2("qello"); s1 = s2; 这样会出问题,因为一个const变量不能随意转化为一个非const变量。
B MyString s1(“hello”); MyString s2(“pello”); MySgtring s3(“qello”); s3 = s1+s2;
不用const也会报错,因为用“+”赋值必须返回一个操作值已知的MyString对象,除非它是一个const对象。
(2)拷贝构造函数可以彼此拷贝对象数组。
(3)类的构造函数里动态的分配了内存,记得要在析构函数里释放
(4)类中包含需要深拷贝的成员时(比如指针),应该自己编写拷贝构造函数和赋值函数
10.6 多态(1)在面向对象语言中,接口的多种不同的实现方式即为多态。多态,意味着一个对象有着多重特征,可以在特定的情况下,表现不同的状态,从而对应着不同的属性和方法。引用Charlie Calverts对多态的描述——多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作(摘自“Delphi4 编程技术内幕”)。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。多态性在Object Pascal和C++中都是通过虚函数(Virtual Function) 实现的。
C++中的多态性具体体现在运行和编译两个方面。运行时多态是动态多态,其具体引用的对象在运行时才能确定。编译时多态是静态多态,在编译时就可以确定对象使用的形式。
C++中,实现多态有以下方法:虚函数,抽象类,覆盖。
(2)重载和重写(覆盖)的区别
重载overload是函数名相同,参数列表不同它是一种语言法则,不是面向对象的特性,在编译期间由编译器将同名函数名称进行修饰。重载与多态无关。
重写override是子类重定义父类的同名虚函数,要求函数名、参数列表和返回值完全相同。与多态有关。是面向对象的特性。
10.7 友元(1)友元函数在类内声明,在类外定义,友元不是成员函数,但可以访问类中的私有成员。其作用是提高程序的运行效率。如果不用友元而是用类的成员函数去访问私有成员,则由于参数传递、类型检查和安全检查等过程需要时间开销,会影响程序的运行效率。
(2)模板类的友元重载举例:
#include
using namespace std;
template < class T>
class Test;
template
ostream& operator <<(ostream& out, const Test
template
class Test
{
private:
int num;
public:
Test(int n=0):num(n){}
Test(const Test
{
num = copy.num;
}
friend ostream& operator<<(ostream &out, const Test
};
template
ostream & operator<<(ostream &out, const Test
{
out<
//out<<"hello";
return out;
}
int main()
{
Test
cout<
return 0;
}