1、已知类String 的原型为:
class String
{
public:
String(const char *str = NULL); // 普通构造函数
String(const String &other); // 拷贝构造函数
~ String(void); // 析构函数
String & operate =(const String &other); // 赋值函数
private:
char *m_data; // 用于保存字符串
};
请编写String 的上述4 个函数。
答案:
String::String(const char *str)
{
if ( str == NULL ) //strlen在参数为NULL时会抛异常才会有这步判断
{
m_data = new char[1] ;
m_data[0] = '' ;
}
else
{
m_data = new char[strlen(str) + 1];
strcpy(m_data,str);
}
}
String::String(const String &other)
{
m_data = new char[strlen(other.m_data) + 1];
strcpy(m_data,other.m_data);
}
String & String::operator =(const String &other)
{
if ( this == &other)
return *this ;
delete []m_data;
m_data = new char[strlen(other.m_data) + 1];
strcpy(m_data,other.m_data);
return *this ;
}
String::~ String(void)
{
delete []m_data ;
}
2、虚函数?重虚函数?
虚函数:为了动态绑定
重虚函数:为了实现接口调用
动态绑定:对象的静态类型:
对象在声明时采用的类型。
对象的动态类型:
目前所指对象的类型。是在运行期决定的。对象的动态类型可以更改,但静态类型无法更改。
静态绑定:
绑定的对象是静态类型,某特性依赖对象的静态类型,发生在编译期。
动态绑定:
绑定的对象是动态类型,某特性依赖对象的动态类型,发生在运行期。
class A
{
virtual void vfun();
};
class B : public A
{
virtual void vfun();
};
class C : public A
{
virtual void vfun();
};
B* pB = new B(); //pB的静态类型是B*,动态类型也是B*
A* pA = pB; //pA的静态类型是A*,动态类型是B*
pA->vfun()和pB->vfun(); //调用的是同一个函数,因为虚函数的地址相同;
//因为有了virtual虚函数特性,pB的动态类型指向B*,因此先在B中查找,找到后直接调用;
3、new带括号和不带括号的区别?
不带括号:如果这个类没有自定义的构造函数,也没有虚函数,则new时,不调用构造函数
因此使用new时别不带括号
4、基类的析构函数为什么要使用虚函数?
防止内存泄漏。想去借助父类指针去销毁子类对象的时候,不能去销毁子类对象。假如没有虚析构函数,释放一个由基类指针指向的派生类对象时,不会触发动态绑定,则只会调用基类的析构函数,不会调用派生类的。派生类中申请的空间则得不到释放导致内存泄漏。
new一个对象时,构造函数先构造父类,再进子类;(像基建)
释放对象时(借助父类指针去销毁子类对象的),如果这个基类析构函数没有加virtual,则直接进入到了父类的析构函数;
如果基类析构函数有virtual修饰,则先析构子类,再析构父类。
class A
{
public:
A()
{
int a = 3;
}
virtual ~A()
{
int a = 3;
}
};
class B : public A
{
public:
B()
{
int b = 3;
}
~B()
{
int b = 3;
}
};
//因为有了virtual虚函数特性,pB的动态类型指向B*,因此先在B中查找,找到后直接调用;
int main()
{
A* paa = new B();
delete paa;
return 1;
}
5、
int k = 2;
int* ptr1, *ptr2;
ptr1 = &k;
ptr2 = &k;
k = *ptr1 + *ptr2;
ptr2 = k; //不能将int类型的值分配到int*类型的实体
ptr1 = ptr2;
k = *ptr1*(*ptr2);
6、关系运算符的结果值
关系运算符的值为真时,结果值都为1。关系运算符的值为假时,结果值都为0。
7、
class A
{
virtual void func1();
void func2();
}
Class B: class A
{
void func1(){cout << "fun1 in class B" << endl;}
virtual void func2(){cout << "fun2 in class B" << endl;}
}
A, A中的func1和B中的func2都是虚函数.
B, A中的func1和B中的func2都不是虚函数.
C, A中的func2是虚函数.,B中的func1不是虚函数.
D, A中的func2不是虚函数,B中的func1是虚函数.
答: A
8、野指针
a、指针变量没有被初始化。指针变量被创建时不会自动生成NULL指针,它的值是随机的,所以要么设为NULL,要么指向合法的内存。
b、指针被释放后,没有置NULL
9、内存布局
class CTest
{
public:
CTest():m_chData(‘\0’),m_nData(0)
{
}
virtual void mem_fun(){}
private:
char m_chData;
int m_nData;
static char s_chData;
};
char CTest::s_chData=’\0’;
a、先找有没有virtual,有则需要建立虚表函数,sizeof(CTest) += 4;
b、啥都没有或者只有成员函数 sizeof(CTest) += 1;
c、static声明的成员变量属于类域,不算入对象中 sizeof(CTest) += 0;
d、以及需要注意对齐法则
10、堆、栈、全局变量、局部静态变量构造顺序
设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为? A B D C
C c;
void main()
{
A*pa=new A();
B b;
static D d;
delete pa;
}
对于类A, 是建立在堆上的对象指针pa, 手动释放
对于类B, 是建立在栈上的对象b,main函数结束就释放
构造的规则是预先构造全局变量。
规则是在同一个文件中的全局变量,先定义的变量先构造。
11、写出下面程序的输出结果
class A
{
public:
void FuncA()
{
printf( "FuncA called\n" );
}
virtual void FuncB()
{
printf( "FuncB called\n" );
}
};
class B : public A
{
public:
void FuncA()
{
A::FuncA();
printf( "FuncAB called\n" );
}
virtual void FuncB()
{
printf( "FuncBB called\n" );
}
};
void main( void )
{
B b;
A *pa;
pa = &b;
A *pa2 = new A;
pa->FuncA(); ( 3)
pa->FuncB(); ( 4)
pa2->FuncA(); ( 5)
pa2->FuncB();
delete pa2;
}
FuncA called FuncBB called FuncA called FuncB called
B b;
A *pa;
pa = &b;
A *pa2 = newA;
pa->FuncA(); ( 3)//pa=&b动态绑定但是FuncA不是虚函数,所以FuncA called
pa->FuncB(); ( 4)//FuncB是虚函数所以调用B中FuncB,FuncBB called
pa2->FuncA(); ( 5)//pa2是A类指针,不涉及虚函数,调用的都是A中函数,所以FuncA called FuncB called
pa2->FuncB()