大数运算在面试与笔试中遇到的概率还是蛮大,上次在大华笔试就遇到过。
题目:编写一个关于大整型无符号数的加法、乘法的类。
#include <iostream>
#include <string>
#include <deque>
#include <functional>
#include <algorithm>
using namespace std;
class MyBigNum
{
private:
deque<int> v; //用deque容器表示的大整数
public:
MyBigNum(){}
MyBigNum(string strNum)//通过字符串建立大整数
{
copy(strNum.begin(),strNum.end(),back_inserter(v));
transform(v.begin(),v.end(),v.begin(),bind2nd(minus<int>(),'0'));//字符'1'变成整数1要减去'0'
}
deque<int>::iterator begin()//迭代始指针
{
return v.begin();
}
deque<int>::iterator end()//迭代止指针
{
return v.end();
}
int size()//容器大小
{
return v.size();
}
back_insert_iterator< deque<int> > Back_Inserter()
{
return back_inserter(v);
}
void push_front(int n)//前插入元素
{
v.push_front(n);
}
void push_back(int n)//后插入元素
{
v.push_back(n);
}
void adjust()//调整使容器每们整型元素值都小于10
{
int nSize=v.size();
for(int i=nSize-1;i>=1;i--)
{
int value=v[i];
if(value<10)
{
continue;
}
v[i]=value%10;
v[i-1]+=value/10;
}
int value=v[0];//处理最高位
if(value>=10)
{
v[0]=value%10;
value=value/10;
while(value>0)
{
v.push_front(value%10);
value/=10;
}
}
nSize=v.size();
}
MyBigNum Add(MyBigNum &m)
{
MyBigNum result;
int n=size()-m.size();
if(n>=0)//若大于等于加数位数
{
transform(begin()+n,end(),m.begin(),result.Back_Inserter(),plus<int>());
for(int i=n-1;i>=0;i--)
{
result.push_front(*(begin()+i));
}
}
else//若小于加数位数
{
transform(begin(),end(),m.begin()-n,result.Back_Inserter(),plus<int>());
}
for(int i=-n-1;i>=0;i--)
{
result.push_front(*(m.begin()+i));
}
result.adjust();//结果调整
return result;
}
MyBigNum Multiply(MyBigNum &m)
{
MyBigNum result("0");
MyBigNum mid;
for(int i=0;i<m.size();i++)
{
mid=*this;
for(int j=0;j<i;j++)//加0相当扩大10倍
{
mid.push_back(0);
}
transform(mid.begin(),mid.end(),mid.begin(),bind2nd(multiplies<int>(),*(m.begin()+i)));//被乘数分别乘以每位乘数
result=mid.Add(result);//分项之和累加
}
return result;
}
};
int main()
{
MyBigNum m1("1234567890");
MyBigNum m2("99999999998");
MyBigNum result=m1.Add(m2);
cout<<"1234567890+99999999998=";
copy(result.begin(),result.end(),ostream_iterator<int>(cout));
cout<<endl;
MyBigNum m3("99");
MyBigNum m4("99999");
MyBigNum m5=m3.Multiply(m4);
cout<<"99*99999=";
copy(m5.begin(),m5.end(),ostream_iterator<int>(cout));
cout<<endl;
return 0;
}
其实这个程序还存在一个很明显的漏洞,这也是上次大华面试官留给我的一个问题。
其实当两个乘数或加数够大时,他们的相加或相乘的结果会超出栈的表示范围,因些解决办法是将他们用堆存储。

今天在阅读C++ primer时遇到如下一段代码:
istream_iterator<int> in_iter(cin);//read ints from cin
istream_iterator<int> eof;//istream "end" iterator
//read until end of file,storing what read in vec
while(in_iter != eof)
{
//increament advances the stream to the nest value
//dereferent reads next value from the istream
vec.push_back(*in_iter++);
}
对于*in_iter++,我们知道++的优先级高于*,所以相当于写成*(in_iter++),意思是对istream_iterator对象做自增运算使该迭代器在流中向前移动。然而,使用后自增运算的表达式,基结果是迭代器原来的值。自增的效果是使迭代器在流中移动到下一个值,但返回值指向前一个值的迭代器。对该迭代器进行解引用获取该值。
为了完全理解这段话,需要深刻了解自增运算符。
自增运算符分为前增运算符++a与后增运算符a++,先看下面代码:
#include <iostream>
using namespace std;
int main()
{
int a=5;
int b=+++++a;
cout<<"b= "<<b<<"\t"<<"a= "<<a<<endl;
return 0;
}
运行结果:
b= 7 a= 7
#include <iostream>
using namespace std;
int main()
{
int a=5;
int b=(a++)++;
cout<<"b= "<<b<<"\t"<<"a= "<<a<<endl;
return 0;
}
运行结果:
发生编译错误:error C2105: '++' needs l-value
我们知道:自增运算符++是结合方向是自右自左(VC++6.0),所以++++a也在写成++(++)a。根据++结合性,a++++肯定是错误的。至于(a++)++和++a++是否会发生错误,分析a++与++a后自会知道。
a++与++a的差别:
(1)在运算过程中,先将对象进行递增修改,而后返回该对象(其实就是对象的引用)的叫前递增运算++a。在运算符重载函数中采用返回对象引用的方式编写。
(2)在运算过程中,先返回原有对象的值,而后进行对象递增运算的叫后递增运算a++。在运算符重载函数中采用值返回的方式编写,重载函数的内部实现必须创建一个用于临时存储原有对象值的对象,函数返回的时候就是返回该临时对象。
现在来分析下上面提出的问题:对于 (a++)++,先运算(a++),但(a++)返回的不是引用,而是原有a值的一个拷贝,而此时的拷贝不再是一个变量,还是一个常量,故不能当作左值继续参加括号外部的++运算。至于++a++,即++(a++),同样不能编译通过,原因是同样的道理。
自增运算符++的重载:
在编写运算符重载函数的时候该如何区分前递增运算符重载函数与后递增运算符重载函数呢?方法就是:在后递增运算符重载函数的参数中多加一个int标识,标记为后递增运算符重载函数。
#include <iostream>
using namespace std;
class Test
{
public:
Test(int a=0)
{
Test::a=a;
}
friend Test& operator++(Test&);
friend Test operator++(Test&,int);
public:
int a;
};
Test& operator++(Test& val)//前递增
{
val.a++;
return val;
}
Test operator++(Test& val,int)//后递增,int在这里只起到区分作用,没有实际意义
{
Test temp(val);//这里会调用拷贝构造函数进行对象的复制工作
val.a++;
return temp;
}
int main()
{
Test test(5);
++++test;
cout<<"test.a= "<<test.a<<endl;
cout<<"后递增情况下临时存储对象的值状态:"<<(test++).a<<endl;
cout<<"test.a= "<<test.a<<endl;
Test test1(5);
(test1++)++;
cout<<"test1.a= "<<test1.a<<endl;
cout<<"前递增情况下临时存储对象的值状态:"<<(++test1).a<<endl;
cout<<"test1.a= "<<test1.a<<endl;
return 0;
}
运行结果:
test.a= 7
后递增情况下临时存储对象的值状态:7
test.a= 8
test1.a= 6
前递增情况下临时存储对象的值状态:7
test1.a= 7
上面代码是非成员格式,在《C++编程惯用法—高级程序员常用方法和技巧》中讲到对所有的一元操作符建议重载操作符函数为成员函数。
#include <iostream>
using namespace std;
class Test
{
public:
Test(int a=0)
{
Test::a=a;
}
Test& operator++();
Test operator++(int);
public:
int a;
};
Test& Test::operator++()//前递增
{
this->a++;
return *this;
}
Test Test::operator++(int)//后递增,int在这里只起到区分作用,没有实际意义
{
Test temp(*this);//这里会调用拷贝构造函数进行对象的复制工作
this->a++;
return temp;
}
int main()
{
Test test(5);
++++test;
cout<<"test.a= "<<test.a<<endl;
cout<<"后递增情况下临时存储对象的值状态:"<<(test++).a<<endl;
cout<<"test.a= "<<test.a<<endl;
Test test1(5);
(test1++)++;
cout<<"test1.a= "<<test1.a<<endl;
cout<<"前递增情况下临时存储对象的值状态:"<<(++test1).a<<endl;
cout<<"test1.a= "<<test1.a<<endl;
return 0;
}
运行结果:
test.a= 7
后递增情况下临时存储对象的值状态:7
test.a= 8
test1.a= 6
前递增情况下临时存储对象的值状态:7
test1.a= 7
在这里注意一点:为什么(test1++)++能编译通过,而上面提到的(a++)++却不能编译通过,这是因为进行(test1++)后,假设临时变量为temp,则temp=test1(原来的test1值),然后进行temp++,其实是进行的是temp.a++,而temp成员a是一个变量,所以可以进行自增运算。
参考:
(1)《30天掌握C++精髓》
(2)http://www.cnblogs.com/hazir/archive/2012/04/16/2451933.html#2356352
copy函数是STL中常用函数之一。
copy函数原型:
template<class InputIterator, class OutputIterator>
OutputIterator copy(
InputIterator _First,
InputIterator _Last,
OutputIterator _DestBeg
);
parameters:
_First
An input iterator addressing the position of the first element in the source range.
_Last
An input iterator addressing the position that is one past the final element in the source range.
_DestBeg
An output iterator addressing the position of the fi irst elementn the destination range.
在这里我们需要注意一点:_Last参数的意思:指向最后一个元素的下一个位置。
Return Value:
An output iterator addressing the position that is one past the final element in the destination range, that is, the iterator addresses _Result+ (_Last – _First ).
Remarks:
The source range must be valid and there must be sufficient space at the destination to hold all the elements being copied.
Because the algorithm copies the source elements in order beginning with the first element, the destination range can overlap with the source range provided the _Last position of the source range is not contained in the destination range. copy can be used to shift elements to the left but not the right, unless there is no overlap between the source and destination ranges. To shift to the right any number of positions, use the copy_backward algorithm.
Examples:
#include <vector>
#include <algorithm>
#include <iostream>
int main()
{
using namespace std;
vector <int> v1, v2;
vector <int>::iterator Iter1, Iter2;
int i;
for ( i = 0 ; i <= 5 ; i++ )
v1.push_back( 10 * i );
int ii;
for ( ii = 0 ; ii <= 10 ; ii++ )
v2.push_back( 3 * ii );
cout << "v1 = ( " ;
for ( Iter1 = v1.begin( ) ; Iter1 != v1.end( ) ; Iter1++ )
cout << *Iter1 << " ";
cout << ")" << endl;
cout << "v2 = ( " ;
for ( Iter2 = v2.begin( ) ; Iter2 != v2.end( ) ; Iter2++ )
cout << *Iter2 << " ";
cout << ")" << endl;
// To copy the first 3 elements of v1 into the middle of v2
copy( v1.begin( ), v1.begin( ) + 3, v2.begin( ) + 4 );
cout << "v2 with v1 insert = ( " ;
for ( Iter2 = v2.begin( ) ; Iter2 != v2.end( ) ; Iter2++ )
cout << *Iter2 << " ";
cout << ")" << endl;
// To shift the elements inserted into v2 two positions
// to the left
copy( v2.begin( )+4, v2.begin( ) + 7, v2.begin( ) + 2 );
cout << "v2 with shifted insert = ( " ;
for ( Iter2 = v2.begin( ) ; Iter2 != v2.end( ) ; Iter2++ )
cout << *Iter2 << " ";
cout << ")" << endl;
return 0;
}
运行结果:
v1 = ( 0 10 20 30 40 50 )
v2 = ( 0 3 6 9 12 15 18 21 24 27 30 )
v2 with v1 insert = ( 0 3 6 9 0 10 20 21 24 27 30 )
v2 with shifted insert = ( 0 3 0 10 20 10 20 21 24 27 30 )
另一个例子:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
int main()
{
int n=10;
vector<int> v;
for(int i=0;i<n;i++)
{
v.push_back(i);
}
random_shuffle (v.begin(),v.end());//把容器内的元素捣乱
copy(v.begin(),v.end(),ostream_iterator<int>(cout,"\n"));
return 0;
}
运行结果:
4
3
0
2
6
7
8
9
5
1
今天在写了模板函数时出现下面情况:
程序如下:
#include <iostream>
#include <string>
using namespace std;
template <class T>
void swap(T &a,T &b)//swap already exist in STL
{
T temp(a);
a=b;
b=temp;
}
int main()
{
string a("hello"),b("world");
//int a=1,b=2;
swap(a,b);
cout<<"a= "<<a<<"\t"<<"b= "<<b<<endl;
return 0;
}
编译竟然报错:
error C2667: 'swap' : none of 2 overload have a best conversion
error C2668: 'swap' : ambiguous call to overloaded function Error executing cl.exe.
overload?重载?我想我只写了一个函数怎么会重载呢?
后面才发现:我虽然只写了一个函数,但STL已经写好swap函数了,所以当然会出现重载了,所以只需要将swap改为my_swap即可。
正确代码如下:
#include <iostream>
#include <string>
using namespace std;
template <class T>
void my_swap(T &a,T &b)//swap already exist in STL
{
T temp(a);
a=b;
b=temp;
}
int main()
{
string a("hello"),b("world");
//int a=1,b=2;
my_swap(a,b);
cout<<"a= "<<a<<"\t"<<"b= "<<b<<endl;
return 0;
}
运行结果:
a= world b= hello
后记:命名有时候不仅是牵涉到是否符合C++命名规则,更要避免与系统重名,还要让别人容易读懂。
this article is about how vptr and virtual table works,and it comes from http://www.dreamincode.net/forums/topic/45816-detail-about-how-vptr-and-virtual-table-works/
Assumption: machine is 32-bit .
Here I am going to explain How Virtual table, Virtual pointer for Virtual functions are internally working.
First we have understand memory layout.
Example 1: How the class's memory layout
class Test
{
public:
int data1;
int data2;
int fun1();
};
int main()
{
Test obj;
cout << "obj's Size = " << sizeof(obj) << endl;
cout << "obj 's Address = " << &obj << endl;
return 0;
}
Output:
Sobj's Size = 8
obj 's Address = 0012FF7C
Note: Any Plane member function does not take any memory.
Example 2: Memory Layout of Derived class
class Test
{
public:
int a;
int b;
};
class dTest : public Test
{
public:
int c;
};
int main()
{
Test obj1;
cout << "obj1's Size = " << sizeof(obj1) << endl;
cout << "obj1's Address = " << &obj1 << endl;
dTest obj2;
cout << "obj2's Size = "<< sizeof(obj2) << endl;
cout << "obj2's Address = "<< &obj2 << endl;
return 0;
}
OUTPUT:
obj1's Size = 8
obj1's Address = 0012FF78
obj2's Size = 12
obj2's Address = 0012FF6C
Example 3: Memory layout If we have one virtual function.
class Test
{
public:
int data;
virtual void fun1()
{
cout << "Test::fun1" << endl;
}
};
int main()
{
Test obj;
cout << "obj's Size = " << sizeof(obj) << endl;
cout << "obj's Address = " << &obj << endl;
return 0;
}
OUTPUT:
obj's Size = 8
obj's Address = 0012FF7C
Note: Adding one virtual function in a class takes 4 Byte extra.
Example 4: More than one Virtual function
class Test
{
public:
int data;
virtual void fun1() { cout << "Test::fun1" << endl; }
virtual void fun2() { cout << "Test::fun2" << endl; }
virtual void fun3() { cout << "Test::fun3" << endl; }
virtual void fun4() { cout << "Test::fun4" << endl; }
};
int main()
{
Test obj;
cout << "obj's Size = " << sizeof(obj) << endl;
cout << "obj's Address = " << &obj << endl;
return 0;
}
OUTPUT:
obj's Size = 8
obj's Address = 0012FF7C
Note: Adding more virtual functions in a class, no extra size taking i.e. Only one machine size taking(i.e. 4 byte)
Example 5:
class Test
{
public:
int a;
int b;
Test(int temp1 = 0, int temp2 = 0)
{
a=temp1;
b=temp2;
}
int getA()
{
return a;
}
int getB()
{
return b;
}
virtual ~Test();
};
int main()
{
Test obj(5, 10);
// Changing a and b
int* pInt = (int*)&obj;
*(pInt+0) = 100;
*(pInt+1) = 200;
cout << "a = " << obj.getA() << endl;
cout << "b = " << obj.getB() << endl;
return 0;
}
OUTPUT:
a = 200
b = 10
If we Change the code as then
// Changing a and b
int* pInt = (int*)&obj;
*(pInt+1) = 100; // In place of 0
*(pInt+2) = 200; // In place of 1
OUTPUT:
a = 100
b = 200
Note: Who sits 1st place of Class : Answer is VPTR
VPTR - 1st placed in class and rest sits after it.
Example 6:
class Test
{
virtual void fun1()
{
cout << "Test::fun1" << endl;
}
};
int main()
{
Test obj;
cout << "VPTR's Address " << (int*)(&obj+0) << endl;
cout << "VPTR's Value " << (int*)*(int*)(&obj+0) << endl;
return 0;
}
OUTPUT:
VPTR's Address 0012FF7C
VPTR's Value 0046C060
NOTE: This VPTR's value is a address of Virtual table. Lets see in next Example.
Example 7:
#include <iostream>
using namespace std;
class Test
{
virtual void fun1()
{
cout << "Test::fun1" << endl;
}
};
typedef void (*Fun)(void);
int main()
{
Test obj;
cout << "VPTR's Address " << (int*)(&obj+0) << endl;
cout << " VIRTUAL TABLE 's Address " << (int*)*(int*)(&obj+0) << endl; // Value of VPTR
cout << "Value at first entry of VIRTUAL TABLE " << (int*)*(int*)*(int*)(&obj+0) << endl;
Fun pFun = (Fun)*(int*)*(int*)(&obj+0); // calling Virtual function
pFun();
return 0;
}
OUTPUT:
VPTR's Address 0012FF7C
VIRTUAL TABLE 's Address 0046C0EC
Value at first entry of VIRTUAL TABLE 0040100A
Test: fun1
Example 8:
class Test
{
virtual void fun1() { cout << "Test::fun1" << endl; }
virtual void func1() { cout << "Test::func1" << endl; }
};
int main()
{
Test obj;
cout << "VPTR's Address " << (int*)(&obj+0) << endl;
cout << "VIRTUAL TABLE 's Address"<< (int*)*(int*)(&obj+0) << endl;
// Calling Virtual table functions
cout << "Value at 1st entry of VTable " << (int*)*((int*)*(int*)(&obj+0)+0) << endl;
cout << "Value at 2nd entry of VTable " << (int*)*((int*)*(int*)(&obj+0)+1) << endl;
return 0;
}
OUTPUT:
VPTR's Address 0012FF7C
VIRTUAL TABLE 's Address 0046C0EC
Value at first entry of VIRTUAL TABLE 0040100A
Value at 2nd entry of VIRTUAL TABLE 004012
Example :9
class Test
{
virtual void fun1() { cout << "Test::fun1" << endl; }
virtual void func1() { cout << "Test::func1" << endl; }
};
typedef void(*Fun)(void);
int main()
{
Test obj;
Fun pFun = NULL;
// calling 1st virtual function
pFun = (Fun)*((int*)*(int*)(&obj+0)+0);
pFun();
// calling 2nd virtual function
pFun = (Fun)*((int*)*(int*)(&obj+0)+1);
pFun();
return 0;
}
OUTPUT:
Test::fun1
Test::func1
Example 10: multiple Inheritance
class Base1
{
public:
virtual void fun();
};
class Base2
{
public:
virtual void fun();
};
class Base3
{
public:
virtual void fun();
};
class Derive : public Base1, public Base2, public Base3
{
};
int main()
{
Derive obj;
cout << "Derive's Size = " << sizeof(obj) << endl;
return 0;
}
OUTPUT:
Derive's Size = 12
Example 11: Calling Virtual Functions in case of Multiple Inheritance
class Base1
{
virtual void fun1() { cout << "Base1::fun1()" << endl; }
virtual void func1() { cout << "Base1::func1()" << endl; }
};
class Base2 {
virtual void fun1() { cout << "Base2::fun1()" << endl; }
virtual void func1() { cout << "Base2::func1()" << endl; }
};
class Base3 {
virtual void fun1() { cout << "Base3::fun1()" << endl; }
virtual void func1() { cout << "Base3::func1()" << endl; }
};
class Derive : public Base1, public Base2, public Base3
{
public:
virtual void Fn()
{
cout << "Derive::Fn" << endl;
}
virtual void Fnc()
{
cout << "Derive::Fnc" << endl;
}
};
typedef void(*Fun)(void);
int main()
{
Derive obj;
Fun pFun = NULL;
// calling 1st virtual function of Base1
pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+0);
pFun();
// calling 2nd virtual function of Base1
pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+1);
pFun();
// calling 1st virtual function of Base2
pFun = (Fun)*((int*)*(int*)((int*)&obj+1)+0);
pFun();
// calling 2nd virtual function of Base2
pFun = (Fun)*((int*)*(int*)((int*)&obj+1)+1);
pFun();
// calling 1st virtual function of Base3
pFun = (Fun)*((int*)*(int*)((int*)&obj+2)+0);
pFun();
// calling 2nd virtual function of Base3
pFun = (Fun)*((int*)*(int*)((int*)&obj+2)+1);
pFun();
// calling 1st virtual function of Drive
pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+2);
pFun();
// calling 2nd virtual function of Drive
pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+3);
pFun();
return 0;
}
OUTPUT:
Base1::fun
Base1::func
Base2::fun
Base2::func
Base3::fun
Base3::func
Drive::Fn
Drive::Fnc
By
Asadullah Ansari
others about vptr and virtual :
(1)http://blog.csdn.net/haoel/article/details/1948051
(2)http://topic.csdn.net/u/20120413/22/4691a553-ab7d-4a5b-b14b-757a2676c328.html?64575
(3)http://blog.csdn.net/hairetz/article/details/4137000
(4)http://www.learncpp.com/cpp-tutorial/125-the-virtual-table/
摘要: 今天意外见识了这个名词是在CSDN帖子里http://topic.csdn.net/u/20120406/03/f59790a9-67b8-479c-9f43-9bc413fae761.html?95101 其实这个困惑早就出现了,记得最早是看到了这篇文章http://www.cnblogs.com/wenzhang/archive/2011/12/30/2308052.html,当时还请教了作者,但始终没有说服自己的理由,今天 再看到确实有种重见老朋友的感觉,所以就上网搜搜这方面的帖子了。 首先来看一个简单的例子吧:#include <iostream>using namespa
阅读全文
摘要: 昨天写一个字符串逆置的程序,开始是这么写的:#include <iostream>using namespace std;char* ReverseWord(char* val);int main(){ char *str="hello"; cout<<ReverseWord(str); return 0;}char* ReverseWord(char* val){ char* right=val+strlen(val)-1; char* left=val; //r* right=val; //while(*right != '\0'
阅读全文
摘要: 这是CSDN上一个我觉得对C++初学者了解虚函数一处很好的题目。#include <iostream>using namespace std;class CA{public: void f(){cout << "CA f()" << endl;} virtual void ff(){cout << "CA ff()" << endl;f();}};class CB : public CA{public : virtual void f(){cout << "CB f()&q
阅读全文
摘要: 在Project Settings→ C/C++→ Category→ Listing Files→ Listing file type→Assembly with Source Code然后重新编译,即可在Debug目录下生成一个.asm文件,即汇编输出文件。参考:http://blog.csdn.net/sky1415/article/details/5173477
阅读全文
摘要: 写下面程序来计算下我的电脑的运算速度:#include <stdio.h>#include <time.h>void Printlocaltime(void){ struct tm *timeptr; time_t secsnow; time(&secsnow); timeptr=localtime(&secsnow); printf("The date is %d-%d-20%02d\n", (timeptr->tm_mon)+1, (timeptr->tm_mday), (timeptr->tm_year)...
阅读全文