1.在申请动态内存时最好设置为指针常量(eg.int*const p=new int[100];),有利于delete时运行不出错。
2.C++除法时如果都为整数则结果也为整数,且商的绝对值为除数和被除数的绝对值做带余除法所得,其符号由除数和被除数的符号决定。
3.注意用sprint_s(char*,"%f",double)转换double为字符指针时,double的所有位数都进来了,可以用strlen亲测。
(字符串于数字转换方法:stoi,stold,stof;to_string;stringstream)
4.当for循环的循环体中出现“未定义标识符”的错误而在条件中确实有定义时,可能for(;;)后多了一个“;”。
5.关于如何基于构造函数来构造一个对象:
(1)类名 对象名;
默认调用“对象名()”或“对象名(默认参数)”这些无参构造函数,在栈内存中存在对象名和实际对象;
(2)类名 对象名(一个或以上个参数);
(注意:参数列表你要填几个就填几个,不用把逗号加全,而且由于有默认值的“向右对齐”,不会出现两个逗号相邻的情况
eg.
)
默认调用相应的构造函数,在栈内存中存在对象名和实际对象的;
(3)类名 对象名();
不调用任何构造函数创建对象,仅在栈内在中存在对象名,不存在实际的对象;
6.关于派生类调用基类构造函数和析构函数的问题:
(1)无论如何,派生类对象都会在实现自身构造前调用基类的构造函数(不管是否有用初始化列表去调用和传参),而且都会在派生类对象
析构后自动调用基类的析构函数。
(2)只有在基类有无参构造函数时,才能免去派生类中构造函数的定义。
7.用类定义引用和指针是不会调用类的构造函数的。
eg.
8.代码中定义指针:
9.如果将数组名(常量)通过实参转到形参,则形参实际上是可以改变的:
eg.
10.类名可以和类外的函数名重合,所以要记住在类外定义类的成员函数一定要用“ 函数类型 类名::函数名(形参表) ”的格式。
11.关于二维数组传参:(eg.int a[row][column];)
(1)二维数组是指向指向整数数组的指针,所以实际是int*[]型,但是如果要传入a作为形参,目前只能用int(*a)[]和int a[][n]。不能写为int**,int*a[]等等。
(2)要传参,可以取地址然后传:function(*a,...),这样的形参是int*型的。
12.关于数组的初始化和sizeof()
(1)初始化用一个个字符初始的,如果少了结束符,唯一影响的就是cout是会因为缺少它而多输出一部分。
13.关于强制转换类型(eg. char a;)
(1)基本类型的转换可以 int(a) 或者 (int)a ;
(2)指针的转换必须通过(void*)且括号只能这么写;
14.当你将数组名作为实参传给形参,则在函数内数组名所赋的变量变成一个指针,所以它的sizeof也为4。
eg.
15.声明指针的错误
eg.
16.关于sizeof()
eg.
17.运算符优先级大概览:
聚组--函数调用--数组下标--一元运算符(.,->,a++,a--,!,~,++a,--a,*,&,sizeof)--二元算术运算符--位移位运算--关系运算符--位运算--逻辑运算符--条件运算符
--赋值运算符--逗号运算符
18.strcpy_s是完全覆盖:(getline()也是完全覆盖)
eg.
19.当多维数组的初始化不完整时,遵循“{}优先,其余补零”的法则。
eg.
PS:如果只有外围的{}那么就按顺序从头填满数组。
20.关于多维数组的输出
(1)
eg.
(2)
而如果是字符指针数组:
eg.
也有同样的效果。(输出地址)
(3)cout和strlen一样,都是靠'\0'来结束操作。
eg.
21.关于重载函数的有意思的地方
eg.
22.关于多维数组定义时的默认情况:
eg.
23.关于初始化对象数组和动态申请
eg.动态:
PS:对象数组初始化就是把一个个构造函数写在{}里面即可。
24.关于类的常成员函数的const声明和重载:
当const在函数名前面的时候修饰的是函数返回值,在函数名后面表示是常成员函数,该函数不能修改对象内的任何成员,只能发生读操作,不能发生写操作。
在重载上:
eg.
25.一个关于类的构造函数的形参能不能和数据成员的标识符一样的问题的实验:
eg.
26.动态联编是声明了虚函数的类与最高继承级的派生类之间形成的。
eg.
27.虚函数是类的首地址。
2.C++除法时如果都为整数则结果也为整数,且商的绝对值为除数和被除数的绝对值做带余除法所得,其符号由除数和被除数的符号决定。
3.注意用sprint_s(char*,"%f",double)转换double为字符指针时,double的所有位数都进来了,可以用strlen亲测。
(字符串于数字转换方法:stoi,stold,stof;to_string;stringstream)
4.当for循环的循环体中出现“未定义标识符”的错误而在条件中确实有定义时,可能for(;;)后多了一个“;”。
5.关于如何基于构造函数来构造一个对象:
(1)类名 对象名;
默认调用“对象名()”或“对象名(默认参数)”这些无参构造函数,在栈内存中存在对象名和实际对象;
(2)类名 对象名(一个或以上个参数);
(注意:参数列表你要填几个就填几个,不用把逗号加全,而且由于有默认值的“向右对齐”,不会出现两个逗号相邻的情况
eg.
...
obj(int i, char*n = "HANDSOME", unsigned short num = 1, unsigned short family_ = 3) { intt = i; name = n; number = num; family = family_; }
...
int main()
{...
obj obj3(1,"H");
...
}
)
默认调用相应的构造函数,在栈内存中存在对象名和实际对象的;
(3)类名 对象名();
不调用任何构造函数创建对象,仅在栈内在中存在对象名,不存在实际的对象;
6.关于派生类调用基类构造函数和析构函数的问题:
(1)无论如何,派生类对象都会在实现自身构造前调用基类的构造函数(不管是否有用初始化列表去调用和传参),而且都会在派生类对象
析构后自动调用基类的析构函数。
(2)只有在基类有无参构造函数时,才能免去派生类中构造函数的定义。
7.用类定义引用和指针是不会调用类的构造函数的。
eg.
...
obj b, b1(2);//只有这里构造了两次
obj*pb;
obj*pb1 = &b1;
obj&rb = b1;
...
8.代码中定义指针:
int a=0;
int*pa=&a;
cout<<*pa++<<endl;
//注意以上“*”和“++”是后者优先,但是记住是先进行*操作再将地址++!( . > ++ > * )
9.如果将数组名(常量)通过实参转到形参,则形参实际上是可以改变的:
eg.
#include<iostream>
using namespace std;
void coutt(int a[])//但不知道g++是怎么样的
{
for (int i = 0; i < 2; i++)
cout << *a++ << endl;
}
void main()
{
int a[] = { 1,2,3 };
coutt(a);
system("pause");
}
10.类名可以和类外的函数名重合,所以要记住在类外定义类的成员函数一定要用“ 函数类型 类名::函数名(形参表) ”的格式。
11.关于二维数组传参:(eg.int a[row][column];)
(1)二维数组是指向指向整数数组的指针,所以实际是int*[]型,但是如果要传入a作为形参,目前只能用int(*a)[]和int a[][n]。不能写为int**,int*a[]等等。
(2)要传参,可以取地址然后传:function(*a,...),这样的形参是int*型的。
12.关于数组的初始化和sizeof()
(1)初始化用一个个字符初始的,如果少了结束符,唯一影响的就是cout是会因为缺少它而多输出一部分。
void main()
{
char a[] = { 'a' }; //miss '\0'
cout << a << endl; //displays a烫烫+軧龈
cout << sizeof(a) << endl; //displays 1
system("pause");
}
(2)如果加了结束符,记住它也占一个字节。
void main()
{
char a[] = { 'a',\0' };
cout << a << endl; //displays a
cout << sizeof(a) << endl; //displays 2
system("pause");
}
13.关于强制转换类型(eg. char a;)
(1)基本类型的转换可以 int(a) 或者 (int)a ;
(2)指针的转换必须通过(void*)且括号只能这么写;
14.当你将数组名作为实参传给形参,则在函数内数组名所赋的变量变成一个指针,所以它的sizeof也为4。
eg.
void f(int a[])
{
cout<<sizeof(a)<<endl; //displays 4
a++; //0 error
}
15.声明指针的错误
eg.
int* a,b,c;//a is a pointer and b,c are numbers
int* a,*b,* c;//a,b,c are pointers
#define 233 char*
233 a,b;//a is a pointer and b is a number
16.关于sizeof()
eg.
int a=0,b=1;
cout<<sizeof(a=b+1)<<endl; //displays 4, the size of the variable a(int)
cout<<a<<endl; //displays 0, showing the assignment(赋值) to a in the expression doesn't actually happen
//因为可以把sizeof()看成预编译,所以预编译完代码变成:
//int a=0,b=1;
//cout<<sizeof(int)<<endl;
//cout<<a<<endl;
//所以当编译器去编译时,a还是0
int f1(){return 0;};
double f2(){return 0.0;}
void f3(){}
cout<<sizeof(f1())<<endl; // f1()返回值为int,因此被认为是int
cout<<sizeof(f2())<<endl; // f2()返回值为double,因此被认为是double
cout<<sizeof(f3())<<endl; // 错误!无法对void类型使用sizeof
cout<<sizeof(f1)<<endl; // 错误!无法对函数指针使用sizeof
cout<<sizeof*f2<<endl; // (原话是:*f2,和f2()等价,因为可以看作object,所以括号不是必要的。被认为是double)但是在vs2017上报错说sizeof的操作数
//不能是函数
double* (*a)[3][6]; //这里a是指向二维的指向double的指针的数组(3*6)的指针
cout<<sizeof(a)<<endl; // 4,因为a是指针
cout<<sizeof(*a)<<endl; // 72,是一个3*6的二维指针数组,等于3*6*4
cout<<sizeof(**a)<<endl; // 24,是第一行指针数组,等于6*4
cout<<sizeof(***a)<<endl; // 4,是第一行第一列的元素,即指针,等于4
cout<<sizeof(****a)<<endl; // 8,上面的指针所指的元素,即double
17.运算符优先级大概览:
聚组--函数调用--数组下标--一元运算符(.,->,a++,a--,!,~,++a,--a,*,&,sizeof)--二元算术运算符--位移位运算--关系运算符--位运算--逻辑运算符--条件运算符
--赋值运算符--逗号运算符
18.strcpy_s是完全覆盖:(getline()也是完全覆盖)
eg.
char s[10] = "123456789";
strcpy_s(s, "hello");
cout << s << endl; //displays hello instead of hello6789
19.当多维数组的初始化不完整时,遵循“{}优先,其余补零”的法则。
eg.
int a[3][3]=
{
{1,2},{},
{3,4,5}
};
for (int i = 0; i < 9; i++)
{
cout << *(*a + i) << " "; //displays 1 2 0 0 0 0 3 4 5
}
PS:如果只有外围的{}那么就按顺序从头填满数组。
20.关于多维数组的输出
(1)
eg.
char str[2][3] = { {'a','b','\0'},{'e','d','s'}};
cout << str << endl;
cout << str + 1 << endl;
cout << *str << endl;
cout << str[1] << endl;
/*displays:
00EFFD78
00EFFD7B //+3
ab //因为'\0'
eds烫烫烫]?C滮 //因为没有'\0',cout停不下来
*/
(2)
而如果是字符指针数组:
eg.
char*str[2] = { "ab","eds" };
cout << str << endl;
cout << str + 1 << endl;
也有同样的效果。(输出地址)
(3)cout和strlen一样,都是靠'\0'来结束操作。
eg.
char st[20] = "hello\n\0";
cout << strlen(st) << endl //displays 6
<< sizeof(st) << endl; //displays 20
cout << st << endl; //hello[Enter]
21.关于重载函数的有意思的地方
eg.
int f(int a, int b)
{
return a + b;
}
float f(float a, float b)
{
return a + b;
}
int main()
{
cout << f(1, 1.2) << endl; //displays 2
cout << f(1.2, 1) << endl; //displays 2
}
//因为函数匹配的优先规则,前一个int f(int,int)更加匹配,所以选它。
22.关于多维数组定义时的默认情况:
eg.
int a[2][3] = { 3,4,5 };//2*3
int aa[][3] = { 3,4,5 };//1*3
23.关于初始化对象数组和动态申请
eg.动态:
class c
{
public:
c(int i, int j, char c) { m = i; n = j; ch = c; }
private:
int m;
int n;
char ch;
};
int main()
{
c*pc = new c(c(2,3,'a'));
delete pc;
c*pccc = new c(3,2,'s');
delete[]pccc;//亦可以 delete pccc
c*pcc = new c[3]{c(2,3,'a'),c(1,2,'v'),c(9,100,'r')};
delete[]pcc;
int*p = new int[3]{ 2,3,4 };
delete[]p;
return 0;
}
PS:对象数组初始化就是把一个个构造函数写在{}里面即可。
24.关于类的常成员函数的const声明和重载:
当const在函数名前面的时候修饰的是函数返回值,在函数名后面表示是常成员函数,该函数不能修改对象内的任何成员,只能发生读操作,不能发生写操作。
在重载上:
eg.
class Test(){
public:
Test(){}
const int foo(int a);
const int foo(int a) const;
};
int main(int argc, _TCHAR* argv[])
{
Test obj;
const Test obj1;
obj.foo(3);//使用非const函数(优先)
obj1.foo(3);//使用const函数
}
//这打破了重载函数必须形参不一样的铁规
25.一个关于类的构造函数的形参能不能和数据成员的标识符一样的问题的实验:
eg.
class point
{
public:
point(int x, int y) { this->x = x; point::y = y; }
private:
int x, y;
};
26.动态联编是声明了虚函数的类与最高继承级的派生类之间形成的。
eg.
class a
{
public:
virtual void show() { cout << "a" << endl; }
};
class aa :public a
{
public:
void show() { cout << "aa" << endl; }
};
class aaa:public aa
{
void show()
{
cout << "aaa" << endl;
}
};
int main()
{
aaa AAA;
a*A = &AAA;
A->show(); //displays aaa(注意:甚至可以访问最高继承级的private成员)
}
27.虚函数是类的首地址。
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
};
typedef void(*Fun)(void);
int main()
{
Base b;
Fun pFun = NULL;
cout << "虚函数表地址:" << (int*)(&b) << endl;
cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;
pFun = (Fun)*((int*)*(int*)(&b));
pFun();
pFun=(Fun)*((int*)*(int*)(&b) + 0); // Base::f()
pFun();
pFun=(Fun)*((int*)*(int*)(&b) + 1); // Base::g()
pFun();
pFun=(Fun)*((int*)*(int*)(&b) + 2); // Base::h()
pFun();
return 0;
}