重拾C++第一天_WDS
1.面向对象编程的三大特点:封装、继承、多态
2.C++中若不指定类中成员的访问权限默认就是private的(class默认是private的,struct默认是public的)。
3.C++规范中类的名字的首字母应该大写。
4.C++中的this是个指针,指向当前类或对象。(注意C++中this是指针,通过this->member访问成员,而Java中的this不是指针,通过this.member访问成员)
5.C++中的就近原则
当传参数名和类的成员名是一样的话,使用的就是参数名,因为比较近。
6.C++中类中"public:"下面的都是public属性的成员,而Java中的类的每个函数前都要加权限修饰。
7.C++和Javac程序对比
/*This_test.java*/
class Person {
String name; public void setName(String name) {
this.name = name;
System.out.println("name="+this.name);
}
}; /*这里写不写";"都行*/ /*注意是这个类中包含main()和注意格式*/
public class This_test {
public static void main(String args[]) {
/* 1.这里直接Person person;来定义对象不行,必须要new Person(),若是有参构造函数()里还需要加参数。
* 2.java中没有指针,C++中的char * 这里要换成String。
* 3.C++中的this是指针,通过this->访问成员,Java中通过.来访问成员。
*/
Person person = new Person();//new Person("mmmm"); C++中只有定义指针时才是这样的,eg Person *p1 = new Person("Zhangshan", 10);
person.setName("ZhangShan");
}
};
/*cpp_this.cpp*/
#include <stdio.h> class Person {
const char *name; public:
void setName(const char *name) {
this->name = name; /*the type of this is "Person* const"*/
printf("name=%s\n", this->name);
}
}; /这里像结构体一样需要有“;”,但是namespace的{}后写不写都行/ int main() {
Person person1; /*使用无参构造函数或有参Person person1("Zhangsan"); 这里不需要像Java语言必须去new一个对象*/
person1.setName("SunFaliang");
}
8.在类外实现类的函数,需要像C语言中声明格式一样在类中声明,然后在类外通过:“返回类型 类名::函数名(参数)”,通过命名空间引用也是使用"::"
9.没有使用命名空间的话,一个源文件要使用一个类,只需要包含这个类的声明的头文件即可。
10.using(非using namespace)后面跟的是对象,而不是命名空间,eg: using P1;报错,using P1::Person;正确(namespace P1{...})。
using P1::Person;表示把P1::Person放到global namespace中,然后就可以直接通过Person代表P1::Person
还可以吧using放到具体函数的内部,eg: 在函数内部使用using P1::Person;就是将P1::Person放到函数内部的local namespace中,
那么此时这个using只在此函数内部有效。
11.还有一种用法:using namespace P1; 表示把P1命名空间中的所有类和函数都导入进来。
using是一个一个地导入,using namespace是把整个命名空间的内容都导入进来。
12.若两个命名空间都定义了同样的函数,而且使用中使用using namespace同时导入了这两命名空间,只要不使用这个函数,也是没有问题的,
也就是说只有使用时链接有冲突了才编译报错。
13.#include <iostream>并且指定std命名空间后就可以使用cout<<进行打印,
using namespace std; cout<<XXX 或 std::cout<<XXX
14.引用,int& b = a; b是a的引用,也称为是a的别名,和a使用相同的地址。引用相当于一个常指针,在定义时必须初始化,eg. int &b; 编译是会报错的。
被引用的对象一定是要有实体的,例如作为非函数参数时int &b = 1; 编译时也会报错的。
15.当时const引用可以使用常量进行初始化,b放入符号表,没有实体,但是在编译时发现有强制取其地址,有会给它分配地址空间
const int &b = 1;
int *p = &b; //error: invalid conversion from ‘const int*’ to ‘int*’
int *p = (int *)&b; //ok
16.构造函数
与类同名,无返回类型,eg Person([参数])
Person per("ZhangSan", 10); //定义一个对象并使用2个参数的构造函数
Person per; //定义一个对象并使用无参数的构造函数
Person per(); //定义一个函数,返回Person ############################################
Person *p1 = new Person();
Person *p2 = new Person; //和上面一样都会调用无参构造函数来初始化对象
Person *p3 = new Person[4]; //指向一个数组,都调用无参构造函数。
Person *p4 = new Person("Zhangshan", 19);
使用完后,delete p1; delete []p3; 注意销毁的也是一个数组。
若是使用new获取的Person对象,没有使用delete释放,就算是整个进程执行结束,也不会调用析构函数,此时操作系统会帮我们释放分配的内存,可以使用free -m查看"free"栏。
17.析构函数
格式:~类名() 无论有多少个构造函数都只能有1个析构函数,而且这个析构函数还是没有参数的。
18.函数内部定义的对象在函数执行完后就会调用析构函数进行释放,但是函数内部new出来的对象是需要手动delete进行释放的。
19.下面代码,若name=NULL, 程序打印完“name= ”就死了!!!!为什么 ?????
cout << "name= " << name << ", age= " << age << ", work= " << work << endl;
20.拷贝构造函数和默认拷贝构造函数
C++编译器会为我们提供默认的什么都不做的无参构造函数和析构函数和默认的仅仅是值拷贝的拷贝构造函数。
一旦用户自己提供了构造函数,编译器提供的无参构造函数就不复存在了。因此定义了有参构造函数有时候还需要定义一个无参构造函数。
eg:定义自己的拷贝构造函数:
Person(Person &per) {
cout << "Person(Person &per)" <<endl;
this->name = new char[strlen(per.name) + ];
memcpy(this->name, per.name, strlen(per.name));
this->name[strlen(per.name)] = '\0'; this->age = per.age; this->work = new char[strlen(per.work) + ];
memcpy(this->work, per.work, strlen(per.work));
this->work[strlen(per.work)] = '\0';
}
Person per2(per1); 会触发调用拷贝构造函数。
注意:之后再执行per2 = per1;时就不会调用拷贝构造函数了,此时应该重载=
21.函数内的静态对象
函数func()内部的 static Person per("ZhangSan", 10); 在函数退出后不会调用其析构函数,第二次及以后进入func()也不会再次调用其构造函数,类似C中的static局部变量。
在main()退出后才会调用其析构函数。
22.全局对象
全局对象会在main()执行入口(执行main()中第一条指令之前)被构造。
33.类中包含其它类的成员的构造函数调用逻辑
会首先调用成员对象的构造函数,然后才调用自己的构造函数。若想在自己的构造函数中构造对象成员,那么需要使用到初始化列表,使用初始化列表传参调用对象成员的构造函数。
此时的构造顺序是先调用初始化列表中的构造函数,然后才会调用自己的构造函数。注意成员对象构造函数调用次序与在初始化列表中出现的先后次序无关,只与在Person类中定义的
次序有关,先定义的先调用。eg:
class {
Father father;
Mother mother;
public:
Person(char *name, int age, char *father_name, int father_age, char *mother_name, int mother_age):Father(father_name, father_age), Mother(mother_name, mother_age) {
...
}
}
析构函数的调用顺序:和构造函数的调用次序刚好相反。
34.参考代码:https://github.com/weidongshan/cpp_projects.git
35.默认参数
eg:
构造函数:Person(char *name, int age, char *work = "none");
定义参数:Person per("ZhangSan", 10);
36.C++代码中使用strlen()还是#include <string.h>
37.类的静态成员
static成员变量和方法是属于类的,而不是属于某个具体的对象的。可以使用"类名::"来直接引用。
class Person {
static int count; //这里只是声明类中有这个静态成员,并没有定义,也没有初始化。
}
在类外且main()之外:
int Person::count = 0; //定义并初始化(其前面不需要再加static修饰),定义成全局的目的是想在所有对象被构造之前初始化count值。它看起来像全局变量,
实际上不是的,它是存在于类的命名空间里面的。
静态成员函数中只能访问静态成员变量,不能访问非静态成员变量,因为非静态成员变量只有类实例化对象后才存在,可能在调用静态成员方法时还没有实例化,就算是实例化了也不知道是哪个实例化对象的。
38.初始化列表
初始化列表除了上面给对象成员进行初始化外还可以对非对象成员变量进行初始化
eg:
class Point {
int x;
int y;
public:
Point() {} //什么都不做的无参构造函数
Point(int x, int y):x(x), y(y) {} /*在初始化列表中对成员变量x和y进行初始化了,所以此例中构造函数就不需要做什么了,函数体为空*/
}
39.友元
友元可以访问类的私有属性。
若Point类设置add()函数为其友元函数,可以直接在类的内部用friend关键字声明add函数,
eg:
class Point {
...
public:
...
friend Point add(Point &p1, Point &p2); /*friend加函数签名*/
}
此后add()就可以像类的成员函数一样直接访问类的私有成员变量了:
Point add(Point &p1, Point &p2) {
Point n;
n.x = p1.x + p2.x;
n.y = p1.y + p2.y;
return n;
}
若一个非类的成员函数中直接操作了类的私有成员,就需要将此函数设置为类的友元。若不想设置成友元,就需要使用getX() setX()类似的方法操作类的私有成员变量。
40.运算符重载
int a = 1, b = 2; int c = a + b; 从某种意义上说,“+”也是一个函数,只不过是编译器内部实现的,既然是函数,那么就可以重载它,于是诞生了运算符重载。
eg:
Point operator+(Point &p1, Point &p2) { /*这里只是重载了"+",若是普通变量相加,由于参数匹配,使用的还是普通的"+"*/
Point n;
n.x = p1.x + p2.x;
n.y = p1.y + p2.y;
return n;
} 疑问:
Point add(Point &p1, Point &p2) {
Point n;
n.x = p1.x + p2.x;
n.y = p1.y + p2.y;
return n; //使用新定义的对象接收的话也不会产生新临时对象(没有调用任何构造函数)。
} 要么不提供拷贝构造函数,要么就要提供成下面样子的拷贝构造函数,必须要有const修饰才行
Point(const Point &p) {
this->x = p.x;
this->y = p.y;
cout <<"Point(const Point &p)"<<endl;
}
需要const修饰的原因:
若是拷贝构造函数中没有加const修饰,那么在定义对象的时候使用const Point p2 = p1; 将会报错,因为const Point p2
无法转换成像拷贝构造函数中的参数const Point &p那样的可读可写的引用。
对于上面函数:
Point n = add(p1, p2); //不会在return n的时候产生临时对象
n = add(p1, p2); //会在return n的时候产生临时对象
应该是编译器做了优化,新定义的对象进行接收的话,add()中Point n使用的这个对象n就是函数外新定义的Point n. 这样才解释的通。
41.对前++和后++运算符重载(++p和p++)
函数签名中通过另增加一个参数来区分重载的是前++还是后++
eg: ++p
Point operator++(Point &p);
eg: p++
Point operator++(Point &p, int a); //但是一般在函数内部不使用arg2
b = ++a; 等效于 a=a+1; b=a; ===>对象加1后再返回这个对象
b = a++; 等效于 b=a; a=a+1; ===>返回这个对象后再让对象加1
在修改过程中发现在operator++()返回时会产生临时变量导致多出一次拷贝构造函数和析构函数被调用,浪费时间。优化方法:
对应前++可以将operator++()改为返回类的引用。对于后++则不可以。
也就是说优先采用前++更高效。
42.不能返回函数内部定义的临时变量(对象)的引用,因为函数执行完之后临时变量(对象)就被销毁了。
43.对运算符<<重载
cout<<m; cout<<n; cout<<m<<n<<endl;都行,说明cout<<返回的一定是cout的引用,所以才能一直这样<<下去。
ostream& operator<<(ostream &o, Point p) {
cout<<"("<<p.x<<","<<p.y<<")"<<endl;
return o;
}
44.既然重载运算符是函数,那么也就可以直接调用
eg:
operator+(pointer1, pointer2);
operator++(pointer1); //调用前++
operator++(pointer1, 0);//调用后++,arg2仅仅是为了在函数签名上与前++不同,可以任意传。
operator<<(cout, p1);
#include <iostream> using namespace std; class Point {
int x;
int y; public:
Point() { cout <<"Point()"<<endl;}
Point(int x, int y) : x(x), y(y) {cout <<"Point(int x, int y)"<<endl;}
Point(const Point &p) {
cout <<"Point(const Point &p)"<<endl;
this->x = p.x;
this->y = p.y;
}
~Point() {cout <<"~Point()"<<endl;} int getX() {return x;}
int getY() {return y;} void setX(int x) {this->x = x;}
void setY(int y) {this->y = y;} friend Point add(Point &p1, Point &p2);
friend Point operator+(Point &p1, Point &p2); friend Point& operator++(Point &p);
friend Point operator++(Point &p, int a);
friend ostream& operator<<(ostream &o, Point &p);
}; Point add(Point &p1, Point &p2) {
Point n;
n.x = p1.x + p2.x;
n.y = p1.y + p2.y;
return n;
} Point operator+(Point &p1, Point &p2) {
Point n;
n.x = p1.x + p2.x;
n.y = p1.y + p2.y;
return n;
} /* b = ++a; ==> a = a + 1; b = a; */
Point& operator++(Point &p) {
cout <<"++p"<<endl;
p.x++;
p.y++; return p;
} /* b = a++; ==> b = a; a = a + 1; */
Point operator++(Point &p, int a) {
cout <<"p++"<<endl;
Point tmp = p;
p.x++;
p.y++; return tmp;
} ostream& operator<<(ostream &o, Point &p) {
cout <<"(" << p.x <<", " << p.y <<")" << endl;
return o;
} int main()
{
Point p1(, );
Point p2(, );
Point m, n; cout <<p1<<p2<< endl;
operator++(p1);
operator++(p2, ); operator<<(cout, p1);
operator<<(cout, p2); m = ++p1;
cout <<m<< endl; n = p2++;
cout <<n<< endl; return ;
}
45.类的成员函数重载运算符
上面介绍的重载运算符重载的都是类外部函数的,函数中直接使用类的私有成员,使用友元。
(1)类内部重载+
在类的外部重载加:Point operator+(Point &p1, Point &p2);
在类的内部重载加:Point operator+(Point &p); //这里只需要1个参数了,因为调用时通常是 对象名.operator+, 本身就已经能提供一个参数了。
eg:
Point operator+(Point &p) {
Point n;
n.x = this->x + p.x;
n.y = this->y + p.y;
return n;
}
使用:
Point m = p1 + p2; /*等效于:Point m = p1.operator+(p2);*/
(2)类内部重载前++
在类的外部重载前++:Point& operator++(Point &p);
在类的内部重载前++:Point& operator++(void); //就不需要参数了
Point& operator++(void) {
this->x++;
this->y++;
return *this;
}
使用:
Point m = ++p; /*等效于:Point m = p.operator++();*/
(3)类内部重载后++
在类的外部重载后++:Point operator++(Point &p, int a);
在类的内部重载后++:Point operator++(int a); //同样根据参数判断是前++还是后++
Point& operator++(int a) {
Point tmp = *this;
this->x++;
this->y++;
return tmp;
}
使用:
Point m = p++; /*等效于:Point m = p.operator++(0);参数任意传*/
比如后++的执行推测编译器的行为:
发现++作用的对象不是普通编译器默认支持的类型,于是查找类中有没有重载++运算符,查找类外有没有重载++运算符,若是在类的内部找到了
就直接调用,若是在类的外部找到了,就以此对象为参数进行调用。类内和类外的是等效的,同时定义会冲突。
#include <iostream> using namespace std; class Point {
int x;
int y; public:
Point() { cout <<"Point()"<<endl;}
Point(int x, int y) : x(x), y(y) {cout <<"Point(int x, int y)"<<endl;}
Point(const Point &p) {
cout <<"Point(const Point &p)"<<endl;
this->x = p.x;
this->y = p.y;
}
~Point() {cout <<"~Point()"<<endl;} int getX() {return x;}
int getY() {return y;} void setX(int x) {this->x = x;}
void setY(int y) {this->y = y;} Point add(Point &p) {
Point tmp;
tmp.x = this->x + p.x;
tmp.y = this->y + p.y;
return tmp;
} Point operator+(Point &p) {
Point n;
n.x = this->x + p.x;
n.y = this->y + p.y;
return n;
} /* b = ++a; ==> a = a + 1; b = a; */
Point& operator++(void) {
cout <<"++p"<<endl;
this->x++;
this->y++; return *this;
} /* b = a++; ==> b = a; a = a + 1; */
const Point operator++(int a) { //here with const or not all ok
cout <<"p++"<<endl;
Point tmp = *this;
this->x++;
this->y++;
return tmp;
}
friend ostream& operator<<(ostream &o, const Point &p);
}; ostream& operator<<(ostream &o, const Point &p) { //因为表达式n++返回的是无名临时对象,只能用const引用指向
cout <<"(" << p.x <<", " << p.y <<")" << endl;
return o;
} int main()
{
Point p1(, );
Point p2(, ); Point m = p1.add(p2); cout << m << endl; Point n = p1 + p2; cout << ++n << endl; cout << n++ << endl; //因为表达式n++返回的是无名临时对象,只能用const引用指向 return ;
}
46.重载=
a. operator=()和拷贝构造函数调用的时机的区别:
Person p2 = p1; 时调用的是拷贝构造函数
p2 = p1; 时调用的是重载的operator=(),若是没有提供重载=,也是可以执行的,此时应该也是简单的值拷贝
在类外重载=时编译报错:constructor.cpp:56:61: error: ‘Person& operator=(const Person&, const Person&)’ must be a nonstatic member function
也就是说对=的重载函数必须作为类的成员函数在类的内部进行重载,不允许在类的外部进行重载!!
参考C++官网:http://www.cplusplus.com/doc/tutorial/templates/
eg在类内部重载=的例子:
Person& operator=(Person &p) {
cout << "Person& operator=(Person &p)" << endl;
if (this->name == NULL) {
this->name = new char[strlen(p.name) + ];
}
memcpy(this->name, p.name, strlen(p.name) + ); if (this->work == NULL) {
this->work = new char[strlen(p.work) + ];
}
memcpy(this->work, p.work, strlen(p.work) + ); this->age = p.age; return *this;
}
47.const对象只能调用const函数
eg:
const对象: const Person p1("zhangshan", 14);
const函数: void printInfo(void) const //也即是在函数签名后面加上const, 用于表明不会修改对象的成员属性。
48.const引用
Point & p = n++; 错
const Point & p = n++; 对
因为表达式n++返回的是无名临时对象,只能用const引用指向.
49.可以直接使用new Stu而不用加(), 这样是调用无参构造函数,eg: Stu *s1 = new Stu;
50. 函数调用前加"::",表示调用的是全局函数,不是类自己的成员函数,下面是调用系统调用open()的例子。
int fd = ::open("./tmp.txt", O_RDWR);
重拾C++第一天_WDS的更多相关文章
- 重拾c++第一天(1):环境配置
时过多年,c++基本不记得了,故在此记录相关重拾记录. 学习语言第一步当然是环境配置了(笑),由于暂无用c++进行大型项目开发的需求,所以先下载dev进行过渡. 安装过程非常简单,值得注意的是配置时选 ...
- 重拾MVC——第一天:数据库连接与SqlDbHelper
这个 SqlDbHelper 是我参考网上的和以前用过的 SqlDbHelper 自己写的一个非常简单的东西,主要是记录自己的学习情况 首先在Web.config中配置数据库连接字符串: <co ...
- 重拾c++第一天(3):数据处理
1.short至少16位:int至少与short一样长:long至少32位,且至少与int一样长:long long至少64位,且至少与long一样长 2.sizeof 变量 返回变量长度 或者s ...
- (二)重拾单片机 第一天 LED灯
由图知道 低电平 亮,高电平 灭 控制第一个 LED1 亮灭程序代码,如下 #include<reg52.h> #define uchar8 unsigned char #define u ...
- 重拾c++第一天(2):基本语法
1.输出方法: cout<<"输出语句" 2.输出时换行为 cout<<endl or "\n" 3.连续赋值是合法的,从右往左依次赋值 ...
- CSS魔法堂:重拾Border之——图片作边框
前言 当CSS3推出border-radius属性时我们是那么欣喜若狂啊,一想到终于不用再添加额外元素来模拟圆角了,但发现border-radius还分水平半径和垂直半径,然后又发现border-t ...
- 重拾C
重拾C,一天一点点_10 来博客园今天刚好两年了,两年前开始学编程. 忙碌近两个月,项目昨天上线了,真心不容易,也不敢懈怠,接下来的问题会更多.这两天调试服务器,遇到不少麻烦. 刚出去溜达了一下,晚上 ...
- 【Java】 重拾Java入门
[概论与基本语法] 取这个标题,还是感觉有些大言不惭.之前大三的时候自学过一些基本的java知识,大概到了能独立写一个GUI出来的水平把,不过后来随着有了其他目标,就把这块放下了.之后常年没有用,早就 ...
- 重拾算法之复杂度分析(大O表示法)
.katex { display: block; text-align: center; white-space: nowrap; } .katex-display > .katex > ...
随机推荐
- HanLP自然语言处理包介绍
支持中文分词(N-最短路分词.CRF分词.索引分词.用户自定义词典.词性标注),命名实体识别(中国人名.音译人名.日本人名.地名.实体机构名识别),关键词提取,自动摘要,短语提取,拼音转换,简繁转换, ...
- 四:(之三)制作镜像和一些docker命令
3.DIY image 3.1如何去掉sudo权限命令,让当前用户拥有操作docker的权限? 3.2 制作一个image: 拉取一个非常小的base image,hello-world.其中是一个可 ...
- Matlab中小语法点总结(更新中)
1. A(:,1)' A(:)' A(:).' A(:,n)' 对矩阵A的低n列进行共轭转置:A(:).'对矩阵A进行转置: 2.subplot的使用方法: h =subplot(m,n,p) ...
- SAC处理命令transfer的一些详细介绍
引自具神的博客: http://seisman.github.io/SAC_Docs_zh/commands/tranfer.html 其中要注意的是用resp文件转换得到的单位直接就是nm/s, 但 ...
- python中变量命名的基本规则,标识符和关键字
变量的命名 目标 标识符和关键字 变量的命名规则 0.1 标识符和关键字 1.1 标识符 标示符就是程序员定义的 变量名.函数名 名字 需要有 见名知义 的效果,见下图:  标示符可以由 字母.下划 ...
- SpringMVC学习一
先看SpringMVC的视图解析 以及 摘录自http://www.cnblogs.com/HigginCui/p/5856780.html的架构解析 1.DisPatcherServlet:前 ...
- Oracle非归档模式下脱机数据文件
正常情况下,要想对数据文件脱机,必须在归档模式下,这是ORACLE自动保护的一种措施,防止在非归档模式下对数据文件脱机,造成数据丢失.如果想在非归档模式下执行数据文件脱机操作,则需要加上“for dr ...
- 关于orm 的基础3 day67
day67 ORM 特殊的语法 一个简单的语法 --翻译成--> SQL语句 语法: 1. 操作数据库表 创建表.删除表.修改表 2. 操作数据库行 增.删.改.查 怎么连数据库: 需要手动创建 ...
- thinkphp5.1学习笔记
由于新公司使用的框架是thinkphp5.1,有必要进一步学习,看来我要把php框架使用完全了,之前就用过laravel和CI框架了. 现在打算只是一个学习的记录,结构会比较凌乱,基本我估计只能自己看 ...
- python 元组攻略
1.元组中只包含一个元素时,需要在元素后面添加逗号来消除歧义 tup1=(50,) 2.元组中的元素值使不允许修改的,但可以对元组进行连接组合复制代码 1 tup1=(12,34.56)2 tup2= ...