C、C++的注意事项

1.sizeof 结构体
    同种数据类型、最长的数据类型、pragma pack(n)

2.优先队列:

取出优先级最小或最大的元素,进行删除和读取    堆作为存储结构,时间可提高到O(log2n)

双端队列:在两端都可删除和插入,而不是队列只能在尾插入,在头删除

3.智能指针:

原理:引用计数,每次创建类的新对象时,初始化指针并将引用计数置为1;当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;调用析构函数时,析构函数减少引用计数(如果引用计数减至0,则删除基础对象)

两个对象,其中一个临时:类TestPtr对象的任何拷贝、赋值操作都会使多个TestPtr对象共享相同的指针。但在一个对象发生析构时,指针指向的对象将被释放,从而可能引起悬垂指针

删除指针后即引用计数为零后才删除对象:其实,智能指针的引用计数类似于java的垃圾回收机制:java的垃圾的判定很简答,如果一个对象没有引用所指,那么该对象为垃圾。系统就可以回收了。

4.long atoi(const char* str)

char *  itoa(int i,char *str,int jinzhi)  _itoa_s(int i,char *str,int len,int jinzhi)

5.各个文件中定义的全局变量名不可相同

在不同的C文件中以static形式来声明同名全局变量。可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错

把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。static函数与普通函数作用域不同。仅在本文件。

A.cpp

extern int i;

int main()

{

i = 56;

}

B.cpp

int i;

6.字符串反转:两个变量

把字符串“I   am   a   student”反转成为“student   a   am   I”  :以' '为出发点,双重循环即可

实现一个string

7.洗牌算法

这个算法的要求是这样的:将N个数乱序后输出.由于和扑克牌的洗牌过程比较相似所以我也就称为洗牌算法了.很多地方都不自觉的需要这个算法的支持.也可以将这个算法扩展为从N个数中取出M个不重复的数(0<M<=N).

       在大四的时候曾经在做某XX系统的时候遇到过这个问题,当时是要从N道题中选出M道然后出题.当时想到的是最简单也是最低效的一种方法:从N个数中随机取 出一个放入一个新的集合中,看集合中是否已经存在这个数,如果没有就继续,否则重新生成随机数.由于当时需要的题目量比较少,因此也就蒙混过关了.后来一 想如果每次生成的随机数都一样岂不是很慢?当时采用的还是”高科技”:HashSet.Java的HashSet有这个一个好处,就是添加的数如果已经存 在就不进行操作,省得再判断是否存在了.唉,用了java就是容易偷懒啊!

算法(Java):

public HashSet getRamdomNumber(int total,int number){ 

//在0到total-1中随机生成number个不重复的随机数

    HashSet h=new HashSet();

    while(h.size()<number){

        Random r=new Random();

        int n=r.nextInt(total);

        //n++;

        h.add((Integer)n);   //不用考虑重复的情况

    }

    return h;

}

    第二种方法是从网上看到的,思路是:将数组的元素进行随机的交换.这个算法我不太好说,因为它的效果和效率都依赖于交换的次数N.次数越多则乱序的程度越高但是相应的效率也会降低.个人不太推荐这个算法,因为N的次数不好确定.

算法:

void shuffle ( int a[], int n )      //洗牌算法

{

    int tmp = 0,p1,p2;

    int cnt = rand() % 1023;

    while (cnt--)   //随机交换两个位置的数,共交换cnt次

    {

           p1 = rand() % n;

           p2 = rand() % n;

          

           tmp = a[p1];

           a[p1] = a[p2];

           a[p2] = tmp;

    }

}

       后来在看Core Java 2的时候看到了一个计算彩票的问题,实际上就是洗牌算法.思路是这样的:从数组中随机取出一个放到一个新的集合中,然后重复这个工作.这样最后原数组为空 而新的集合则为乱序.这个算法比第一种算法的效率高多了,而且计算次数是固定的.当时就以为找到了最佳算法.后来蒙朋友提醒,对这个算法进行了改进.其实 没有必要开辟一个新的集合的,用原来的存储空间就行.具体思路是:从数组的数中随机取出一个和最后一个元素交换,再从前面N-1个数中随机取一个和倒数第 二个交换…这样可以达到和前面算法相同的效果而且存储空间也得到了节省,很不错的算法了.

算法:

void shuffle()

{

       int *prev=new int[54];

       for(int i=0;i<54;i++)      //数组的初始化,表示54张牌

      {

             prev[i]=i;

      }

       srand(time(0));

       int times=53;

       while(times!=0)

       {

              int one=rand()%times;

              swap(prev[one],prev[times]);       //交换元素

              times--;

       }    

       //这个时候prev数组处于乱序状态,用完后回收空间

       delete prev;

}

        本来到这里就接近尾声了,但是后来看到stl中还提供了random_shuffle()方法.这个方法的效果就是实现洗牌算法.具体的效率方面我还没有测试过,不知道我的算法和它的哪个更快.以后有机会试一下.也许它用的就是我的思路也说不定,哈哈.


8.统计数组中出现最多的数:times,ch,循环            哈希表

/*n个字符的数组,只有一个元素出现的次数最多,找出该元素*/
char FindIt(char a[],int n)
{
  char ch;
  int times=0;
  int i;
for(i=0;i<n;i++)
{
if(times==0)/*计数清零,从本次开始选取新的字符*/
{
ch=a[i];
times=1;
}
else if(ch==a[i])/*找到相同的计数加1*/
times++;
else/*与备选的字符ch不同,计数-1.做一次抵消。只有次数最多的才会保留到最后*/
times--;
}
return ch;
}

9.寻找最大的K个数:哈希后排序(如果有重复的数据)

10.判断一个单链表是否存在循环

p=head
q=head
while(p && q && q->next)
{
p=p->next; q=q->next->next
if (p==q)
  return true
}
return 0;

11.x==(y=z)  cout<<x<<endl;      x并未改变

printf从右到左压栈

float a = 1.0f;

cout<<(int &)a<<endl;输出的不是1,因为整数和浮点数的存储方式不一样,这里相当于把浮点数的前sizeof(int)输出

!(x&(x-1))判断x是否是2的n次方

a、b中数据较大的:(a+b + abs(a-b))/2    不用if

a、b交换不用中间变量:a=a+b;b=a-b;a=a-b;    a=a^b;b=a^b;a=a^b;

#ifndef、#define、#endif:防止头文件被重复引用

const和#define:类型安全、对const调试

修饰为const的成员函数:修改成员变量可用mutable修饰

string有28字节

一个空类占1字节,虚基类占4字节

const常量必须初始化

void GetMemory(char **p,int num)

{

*p = (char *)malloc(sizeof(char)*num);

}//char *str = NULL;GetMemory(&str,100);strcpy(str,"hello");  字符串很特殊,不像int

char *GetMemory(char *p,int num)

{

p = (char *)malloc(sizeof(char)*num);这里的p是分配的全局数组,存储在静态存储区

return p;

}//char *str = NULL;str=GetMemory(str,100);strcpy(str,"hello");

指向常量的指针:int *const  i;

int (*(*f)(int,int))(int):f是函数指针,其返回值也是个函数指针

vector:push_back、pop_back(可能会调用拷贝构造函数)、vector<int>::iterator、ver.begin()、ver.size()

struct和class唯一的不同是默认的访问控制是public

初始列表的初始化是根据 成员变量的声明顺序来执行的

每个虚函数的对象都需维护一个v表

单个参数的构造函数如果不添加explicit关键字,会定义一个隐含的类型转换如int到class

assert false:assert是Debug版本的宏、断言

12.字符串:

整数转字符串

int num = 4678788;
while (num)
{
ch[i] = num % 10 + '0';
i++;
num = num / 10;
}

字符串转整数

char *ch = {"64388"};
while (ch[i]!='\0')
{
num = num*10 + ch[i]-'0';
i++;
}

字符串拷贝:

char *strcpyfun(char *des, const char*sour)
{
assert((des != NULL) && (sour != NULL));
char *ret;
while ((*des++=*sour++)!='\0');
ret = des;
return ret;
}

13. 代码规范:空格、int *t、注释/****/、命名(前缀、名词、m_ s_ g_)

14.迭代器失效:重新分配内存     map实现:红黑树实现的,红黑树是一种自平衡二叉查找树。

序列性容器::(vector)

 
erase 迭代器不仅使所有指向被删元素的迭代器失效,而且使被
 
删元素之后的所有迭代器失效,所以不能使用 erase(iter++)
式,但是 erase 的返回值为下一个有效的迭代器,所以
 
  正确方法为::
 
  for(  iter  =  c.begin ();  iter  !=  c.end (); )

            iter = c.erase(iter);

 

 

关联性容器::(map)
   erase 迭代器只是被删元素的迭代器失效,但是返回值为 void
 
  所以要采用 erase(iter++) 的方式删除迭代器  
  正确方法为::

    for( iter = c.begin(); iter != c.end(); )  

                c.erase(iter++);

 

Tips:

 

其实对于list两种方式都可以正常工作


15.内存初始化、越界、释放(delete后指针要设为NULL,杜绝野指针)

char *pChar = "hello";
char *pValue = new char[strlen(pChar)+1];
strcpy_s(pValue,6,pChar);
pValue[0] = 'H';
printf("%s\n",pValue);

释放了内存,释放了指针

void Func(void) //处理内存耗尽,找不到多余的内存,返回NULL

A *a = new A; 
if(a == NULL) 

  cout << “Memory Exhausted” << endl; 
  exit(1); //出错处理程序

  ⋯ 

16.

(1)函数 Derived::f(float)覆盖了 Base::f(float)。 
(2)函数 Derived::g(int)隐藏了 Base::g(float),而不是重载。 
(3)函数 Derived::h(float)隐藏了 Base::h(float),而不是覆盖。 

c语言不支持函数重载

17. C++ 程序中,应该用内联函数取代所有宏代码,“断言 assert” 恐怕是唯一的例外

内联:宏、函数定义时inline、类

18.delete后要设为NULL

class String
{
public:
String(const char *ch = NULL);
String(const String &sValue);
String &operator =(const String &sValue);
~String(void);


private:
char *m_chValue;
};

String::String(const char *ch)
{
if (ch == NULL)
{
m_chValue = new char[1];
*m_chValue = '\0';
}
else
{
int strlength = strlen(ch);
m_chValue = new char[strlength + 1];
strcpy(m_chValue, ch);
}
}
String::String(const String &sValue)
{
int strlength = strlen(sValue.m_chValue);
m_chValue = new char[strlength + 1];
strcpy(m_chValue, sValue.m_chValue);
}
String &String::operator =(const String &sValue)
{
if (this == &sValue)
return *this;
delete[] m_chValue;

int strlength = strlen(sValue.m_chValue);
m_chValue = new char[strlength + 1];
strcpy(m_chValue, sValue.m_chValue);
return *this;
}
String::~String()
{
delete[] m_chValue;
}

19.

对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”, 目的是提高效率。例如将 void Func(A a) 改为 void Func(const A &a)。  
对于内部数据类型的输入参数,不要将“值传递”的方式改为“const 引用传递”。否 则既达不到提高效率的目的,又降低了函数的可理解性。例如 void Func(int x) 不应 该改为 void Func(const int &x)。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值