原文链接:http://blog.csdn.net/richerg85/article/details/7592842
基本的父子类:
- #include <iostream>
- using namespace std;
- class A
- {
- public:
- A()
- {
- cout<< "A constructor" <<endl;
- }
- virtualvoid Display()
- {
- cout<<"A display"<<endl;
- }
- virtual~A()
- {
- cout<<"A discontructor"<<endl;
- }
- };
- class B:public A{
- public:
- B(int i)
- {
- m_i= i;
- cout<<"B constructor,i="<<i<<endl;
- }
- voidDisplay()
- {
- cout<<"B display"<<endl;
- }
- virtual~B()
- {
- cout<<"B disconstructor"<<endl;
- }
- private:
- intm_i;
- };
主函数调用:
测试1:
- int main()
- {
- B*a = new B(20);//直接派生类
- a->Display();
- deletea;
- }
① B *a =newB(20); 创建一个派生类对象指针,这一条语句执行,会调用
A constructor
B constructor,i=20
这符合构造函数调用顺序:先调用基类的构造函数,然后调用子类的构造函数。
② a->Display();
由于只是调用派生类B的Display函数
B display
③创建的指针对象a,不用后要删除掉:delete a; 从程序中可以看到基类A、派生类B中的析构函数前都有virtual关键字,但是在此实例中,无论析构函数前有没有virtual关键字,执行的结果都为
B disconstructor
A disconstructor
基类声明的虚函数,在派生类中也是虚函数,即使不再使用virtual关键字。
测试2(这个需要和测试进行比较看):
- int main()
- {
- A*b = new B(9);
- b->Display();
- deletea;
- }
① A *b =newB(9); 通过父类new出来子类,这一条语句执行,会调用
A constructor
B constructor,i=9
这符合构造函数调用顺序:先调用基类的构造函数,然后调用子类的构造函数。
② b->Display();由于基类A中的此函数前面有修饰符virtual,则此函数为虚函数,如果子类有同名的函数(B类中有(virtual) void Display()),则会执行子类的Display函数(即子类的函数覆盖了基类的函数)
B display
如果基类A中的Display函数去掉virtual关键字,则会执行
A display
③创建的指针对象a,不用后要删除掉:delete b; 从程序中可以看到基类A、派生类B中的析构函数前都有virtual关键字,执行的结果为
B disconstructor
A disconstructor
如果A中的virtual关键字去掉,则执行结果为
A disconstructor
基类的析构函数都必须是virtual的.
测试3:
- int main()
- {
- Bc(12);
- c.Display();
- }
此调用中,没有使用new创建对象指针,直接调用派生类的构造函数。
① B c(12);此句调用
A constructor
B constructor,i=12
和new出来的没有什么区别。
② c.Display();这个应该也没有什么疑义,和测试1中调用是一致的
B display
但是,从执行的结果来看,跟随在其后还有执行结果
B disconstructor
A disconstructor
可见,这样对象被认为是局部对象,当退出这个对象存在的范围时,会自动执行析构函数。
测试4(同理测试3):
- int main()
- {
- for(int i=0;i<2;i++)
- {
- Bc(i);
- c.Display();
- }
- }
执行结果显示为:
A constructor
B constructor,i=0
B display
B disconstructor
A disconstructor
A constructor
B constructor,i=0
B display
B disconstructor
A disconstructor
读C++ Primer 之构造函数陷阱 http://www.linuxidc.com/Linux/2011-08/40176.htm
读C++ Primer 之构造函数陷阱
- /*
- * author:xizero00
- * mail: xizero00@163.com
- * date:2011-08-07 21:00:59
- * constructor trap
- */
- #include <iostream>
- using namespace std;
- class Base
- {
- public:
- //默认构造函数
- Base(): d( 0 ) {}
- //带一个参数的构造函数
- Base( int val ) : d( val ) { cout << "基类构造函数: val = " << val << endl; }
- public:
- int d;
- };
- class Inherit : public Base
- {
- public:
- //默认构造函数
- Inherit():Base() {}
- //带一个参数的构造函数
- //在执行此构造函数的时候会调用基类的相应的构造函数
- //但是,问题就出在这个继承下来的d,它并没有被初始化,这里必须要注意!
- Inherit( int v ): Base( v + d ) { cout << "子类构造函数: v = " << v << " d = " << d << endl; }
- };
- int main( int argc , char **argv )
- {
- Inherit i( 1 );
- return 0;
- }
将文件存为trap.cc
编译命令为:
- g++ trap.cc -o trap -g
执行结果为:
- 基类构造函数: val = 12382197
- 子类构造函数: v = 1 d = 12382197
C++内存分配与复制构造函数笔试考察
http://www.linuxidc.com/Linux/2015-02/112883.htm
去参加笔试了,有两道题做错了,都是印象里面有概念,但是没有弄清楚它到底是怎么回事,原理是什么,导致题目打错,现总结一下。
一、C++内存分配笔试考察
问题考察如下,请先不要看答案,看看你能否做对,呵呵:
怎么样,晕了没?正确答案及解析如下:
解析:char p[] = “...”是一个数组,这个数组是局部变量。char *p = “...”,是一个指针,这个指针指向一个字符串常量。区别在于:数组的话,字符串是存在这个数组里的,因为这个数组属于局部变量(存在栈区),而当该函数执行完,位于栈区的局部变量就销毁了,就算把数组的地址返回给主函数,主函数也无法访问到原有字符串了,应该输出乱码。但是,如果是指向字符串常量的指针,这个字符串是放在程序的常量区而不是放在局部变量中,那么把这个常量的地址返回给主函数,主函数也是可以访问它的。
下面就针对C++中的内存分配做个总结:
一个C/C++编译的程序占用的内存分为以下几个部分:
1. 栈区(stack)
由编译器自动分配释放,存放函数地址、函数参数值、局部变量的值等。
2. 堆区(heap)
就是那些由new分配的内存块,他们的分配与释放由程序员负责,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
3. 全局/静态区(static)
全局变量和静态变量的存储是放在一块的,初始化的全局变量和初始化的静态变量在一块区域,为初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
4. 常量存储区(const)
常量字符串就是放在这里的。
5. 程序代码区
存放函数体的二进制代码。
其中,堆栈的主要区别如下:
爽歪歪了吧,下面就来看第二个问题吧。。。
复制构造函数 与 赋值函数 的区别
http://www.cnblogs.com/kaituorensheng/p/3245522.html
构造函数、析构函数、赋值函数是每个类最基本的的函数。每个类只有一个析构函数和一个赋值函数。但是有很多构造函数(一个为复制构造函数,其他为普通构造函数。对于一个类A,如果不编写上述四个函数,c++编译器将自动为A产生四个默认的函数,即:
- A(void) //默认无参数构造函数
- A(const A &a) //默认复制构造函数
- ~A(void); //默认的析构函数
- A & operator = (const A &a); //默认的赋值函数
既然能自动生成函数,为什么还需要自定义?原因之一是“默认的复制构造函数”和"默认的赋值函数“均采用”位拷贝“而非”值拷贝“
位拷贝 v.s. 值拷贝
为便于说明,以自定义String类为例,先定义类,而不去实现。
#include <iostream> using namespace std; class String { public: String(void); String(const String &other); ~String(void); String & operator =(const String &other); private:
char *m_data;
int val; };
位拷贝拷贝的是地址,而值拷贝拷贝的是内容。
如果定义两个String对象a, b。当利用位拷贝时,a=b,其中的a.val=b.val;但是a.m_data=b.m_data就错了:a.m_data和b.m_data指向同一个区域。这样出现问题:
- a.m_data原来的内存区域未释放,造成内存泄露
- a.m_data和b.m_data指向同一块区域,任何一方改变,会影响到另一方
- 当对象释放时,b.m_data会释放掉两次
因此
当类中还有指针变量时,复制构造函数和赋值函数就隐含了错误。此时需要自己定义。
结论
- 有一种特别常见的情况需要自己定义复制控制函数:类具有指针哈函数。
- 赋值操作符和复制构造函数可以看成一个单元,当需要其中一个时,我们几乎也肯定需要另一个
- 三法则:如果类需要析构函数,则它也需要赋值操作符和复制构造函数
注意
- 如果没定义复制构造函数(别的不管),编译器会自动生成默认复制构造函数
- 如果定义了其他构造函数(包括复制构造函数),编译器绝不会生成默认构造函数
- 即使自己写了析构函数,编译器也会自动生成默认析构函数
因此此时如果写String s是错误的,因为定义了其他构造函数,就不会自动生成无参默认构造函数。
复制构造函数 v.s. 赋值函数
#include <iostream> #include <cstring> using namespace std; class String { public: String(const char *str); String(const String &other); String & operator=(const String &other); ~String(void); private: char *m_data; }; String::String(const char *str) { cout << "自定义构造函数" << endl; if (str == NULL) { m_data = new char[1]; *m_data = '\0'; } else { int length = strlen(str); m_data = new char[length + 1]; strcpy(m_data, str); } } String::String(const String &other) { cout << "自定义拷贝构造函数" << endl; int length = strlen(other.m_data); m_data = new char[length + 1]; strcpy(m_data, other.m_data); } String & String::operator=(const String &other) { cout << "自定义赋值函数" << endl; if (this == &other) { return *this; } else { 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(void) { cout << "自定义析构函数" << endl; delete [] m_data; } int main() { cout << "a(\"abc\")" << endl; String a("abc"); cout << "b(\"cde\")" << endl; String b("cde"); cout << " d = a" << endl; String d = a; cout << "c(b)" << endl; String c(b); cout << "c = a" << endl; c = a; cout << endl; }
执行结果
说明几点
1. 赋值函数中,上来比较 this == &other 是很必要的,因为防止自复制,这是很危险的,因为下面有delete []m_data,如果提前把m_data给释放了,指针已成野指针,再赋值就错了
2. 赋值函数中,接着要释放掉m_data,否则就没机会了(下边又有新指向了)
3. 拷贝构造函数是对象被创建时调用,赋值函数只能被已经存在了的对象调用
注意:String a("hello"); String b("world"); 调用自定义构造函数
String c=a;调用拷贝构造函数,因为c一开始不存在,最好写成String c(a);
http://blog.chinaunix.net/uid-705824-id-2679635.html
嗯,这是我从学校bbs精华区整理的c/c++及数据结构的笔试以及部分面试题集锦,觉得只要搞懂了这些题目,大部分的笔试都不惧了。希望能给需要的人带来帮助。顺便攒rp ^_^
不过大部分题目没有答案,欢迎补充哦~~~~
一:
已知类String的原型为:
class String
{
public:
String(const char *str = NULL); //普通构造函数
String(const String ©); //拷贝构造函数
~String(void); //析构函数
String & operator = (const String ©); //赋值构造函数
private:
char * m_data; //用于保存字符串
};
请编写String的上述4个函数。
答案:
版本1
// String 的析构函数
String::~String(void) // 3 分
{
delete [] m_data;
// 由于m_data 是内部数据类型,也可以写成delete m_data;
}
String::String(const char *str)
{
if(str==NULL)
{
m_data = new char[1]; // 若能加NULL 判断则更好
*m_data = ‘{post.content}’;
}
else
{
int length = strlen(str);
m_data = new char[length+1]; // 若能加NULL 判断则更好
strcpy(m_data, str);
}
}
// 拷贝构造函数
String::String(const String &other)
{
int length = strlen(other.m_data);
m_data = new char[length+1]; // 若能加NULL 判断则更好
strcpy(m_data, other.m_data);
}
// 赋值函数
String & String:operate =(const String &other)
{
// (1) 检查自赋值
if(this == &other)
return *this;
// (2) 释放原有的内存资源
delete [] m_data;
// (3)分配新的内存资源,并复制内容
int length = strlen(other.m_data);
m_data = new char[length+1]; // 若能加NULL 判断则更好
strcpy(m_data, other.m_data);
// (4)返回本对象的引用
return *this;
}
版本2
String::String (const char *str)
{
if(str){
memset(m_data,0,strlen(m_data));
strcpy(m_data,str);
}
else *m_data=0;
}
String::String (const String ©)
{
strcpy(m_data,copy.m_data);
}
String& String:operator =(const String ©)
{
if(this==©) retrun *this;
strcpy(m_data,copy.m_data);
return *this;
}
版本3
String::String (const char *str)
{
if ( m_data )
delete[] m_data;
if(str){
m_data = new char[strlen(str)];
memset(m_data,0,strlen(m_data));
strcpy(m_data,str);
}
else *m_data=0;
}
String::String (const String ©)
{
if ( m_data )
delete[] m_data;
m_data = new char[strlen(copy.m_data+1)]
strcpy(m_data,copy.m_data);
}
String& String:operator =(const String ©)
{
if(this==©) retrun *this;
if ( m_data )
delete[] m_data;
m_data = new char[strlen(copy.m_data+1)]
strcpy(m_data,copy.m_data);
return *this;
}
~String::String(void)
{
if ( m_data )
delete[] m_data;
}
二:改错题,只能在原来的基础上增加代码,不能删除代码
#include
#include
void foo(int age,char *b)
{
b = (char *)malloc(64);
sprintf(b,"Your Age is %d",age);
}
int main()
{
char *f;
foo(23,f);
printf("%s\n",f);
}
答案
版本1
#include
#include
void foo(int age,char **b)
{
*b = (char *)malloc(64);
sprintf(*b,"Your Age is %d",age);
}
int main()
{
char **f;
foo(23,f);
printf("%s\n",**f);
return 0;
}
版本2
#include
#include
void foo(int age,char *&b)
{
b = (char *)malloc(64);
sprintf(b,"Your Age is %d",age);
}
int main()
{
char *f;
foo(23,f);
printf("%s\n",f);
free(f);//不要忘了free;
}
三:有程序片断如下
int main()
{
int I = 20;
pid_t pid = 5;
if((pid = fork()) > 0)
{
I = 50;
printf("%d\n",I); (1)
}
else if(pid == 0)
{
printf("%d\n",I); (2)
}
}
请问该程序用的是进程方式还是线程方式,并说明进程与线程的区别:
请问该程序输出什么结果?
无参考答案L
四、constant pointer points for String
pointer points for constant string
五、下面等价的是:
A int i=0
if(i)
{
printf("hello,world");
}
B int i=1;
int j=2;
if(i==1 || j==2)
{
printf("hello,world");
}
C Boolean b1=true;
Boolean b2=true;
if(b1==b2)
{
printf("hello,world");
}
D int i=1;
int j=2;
if(i==1 &| j==2)
{
printf("hello,world");
}
六、排序二叉树插入一个节点或双向链表的实现
四~六为IBM面试题。
七、指针++的含义和用法
八、stack 和heap的分配,rt-os的特点、同步的方式
九、怎样避免内存泄漏的问题
十、编程实现十进制数转化为十六进制输出,不准用任何已经定义的库函数,比方说String
,Math。int toHex(int )
十一、编程实现大于100的两个数值相乘的结果输出,同样不准使用任何已定义函数,Math,st
ring,convert等。比方说12345*32534677
输入为两个string int toPlus('12345','32434677')
输出为一个长型的
十二、int delete(node * head)
{
free(head);
head=head->link;
return(0);
}
指出程序的错误,并且写出正确的程序
十三、写一个程序可以算出字节在计算机中的存储是由大到小还是有小到大。
十四、一段程序,写出输出结果
大概是
class A
{
static void virtual print(){cout<<"A::print()"<;
}
class B
{
static void virtual print(){cout<<"B::print()"<;
}
class C
{
static void print(){cout<<"C::print()"<;
}
print (A a)
{
a.print();
}
main()
{
A a,*aa,*ab,*ac;
B b;
C c;
aa=&a;
ab=&b;
ac=&c;
a.print();
b.print();
c.print();
aa.print();
ab.print();
ac.print();
print(a);
print(b);
print(c);
}
十五、给两个变量,如何找出一个带环单链表中是什么地方出现环的。(答案参考expert C programming)。
十~十五为MS笔试题。
十六、写一个带参数宏get_struct_addr_from_member_addr(p, stru, m),
能够根据任意结构实体的某一个成员的地址,算出该结构实体的地址,其中参数p是指向该
成员的指针,stru是该结构体,m是该成员。(SUN试题)
十七、给一个函数
int main(){
int i, n=20;
for(i=0;i
printf("-");
return 0;
}
要求替换、增加或者减少一个字符,使该程序可以打出20个“-”号,并要求写出三种解法
。(sun试题)
参考:1。i-- 换成 n--
2。i换成 -i
十八、解释 typedef char (*FUNC)(int, char*)的含义
十九、问#include 和#include "abc.h"的区别,#define MAX_NUM 10 和 const int
MAX_NUM=10区别
二十、问用什么方法可以避免一些潜在错误,比如if( myvar = 3)这一类
编程规范的问题,用 if( 3 = myvar)就可以在编译时报错
十六~二十为SUN笔试题。