(1)hello world代码
main.cpp文件
//包含c++输出输出流的头文件,iostream.h
//c++中头文件的命名可以不再写.h的后缀,比如这里的iostream和test
//#include "iostream"
#include <iostream>
#include "test"
//c++兼容c语言
#include <stdio.h>
using namespace std;
//使用命名空间,std表示标准的命名空间,在这个命名空间定义了很多标准定义
// void main()
// error: '::main' must return 'int'
int main()
{
// c语言用 <stdio.h>中的printf函数输出到控制台
printf("hello world\n");
// c++用输入输出流
// cout是C++里std中iostream中的标准输出
// endl 等于 \n 的作用,同时刷新缓冲区
cout << "hello world" << endl; // 打印hello world
// 连续打印多个表达式
cout << "hello world" << "\txxx"
<< "\tyyy" << endl;
// << 左移操作符 在c++里面,操作符功能的增强===>C++ 语言操作符重载
cout << age << endl;
return 0;
}
test文件
int age = 100;
编译和运行
[root@centos1 test]# g++ main.cpp -o main
[root@centos1 test]# ./main
hello world
hello world xxx yyy
100
[root@centos1 test]#
(2)cpp中不同类型需强转后才可赋值
#include <stdio.h>
#include <stdlib.h>
int main()
{
//char *p = malloc(100);
//error: invalid conversion from 'void*' to 'char*' [-fpermissive]
//这里malloc函数的返回类型是void *,⽽左值是char *,因此不可赋值
// 需要强转类型才行
char *p = (char *)malloc(100);
free(p);
return 0;
}
(3)cpp⽤new和delete在堆中分配内存
c++语⾔可以兼容c语⾔的内存管理机制
但如果⽤malloc分配的内存就必须要free
如果⽤new分配的就必须⽤delete
不能混合使⽤
int main()
{
// char *p = (char *)malloc(100);
char *p = new char[100]; //在堆中分配100个char
delete p; //释放了第一个char
delete[] p; //释放了一个数组
char *p1 = new char; //在堆中分配一个char
delete p1; //释放了一个char
return 0;
}
delete 和 delete []的区别
- delete 释放new分配的单个对象指针指向的内存,
new
和delete
需配对使用 - delete[] 释放new分配的对象数组指向的内存,
new[]
和delete[]
需配对使用
char *p = new char[100]; //在堆中分配100个char
delete p; //释放了第一个char
delete[] p; //释放了一个数组
(4)const的增强
1.const在c++中是真常量
以下代码在c中通过地址赋值改变了const常量的值
而如果在cpp中则⽆法改变
#include <stdio.h>
int main()
{
// int const a = 0; // 两种写法一样
const int a = 0;
int *p = (int *)&a;
*p = 100;
printf("%d\n", a); //在c中为100 //在cpp中仍然为0
return 0;
}
回顾const关键字在函数形参时的作用
#include <iostream>
using namespace std;
struct Teacher
{
char name[64];
int age;
};
// 指针所指向的内存空间,不能被修改
int operatorTeacher01(const Teacher *pT)
{
//pT->age = 10; // error
return 0;
}
//指针变量本身不能被修改
int operatorTeacher02( Teacher * const pT)
{
pT->age = 10;
//pT = NULL; //error
return 0;
}
//双重属性的常量
int operatorTeacher03( const Teacher * const pT)
{
//pT->age = 10; // error
//pT = NULL; // error
printf("age:%d", pT->age);
return 0;
}
int main()
{
return 0;
}
2.const和#define的区别
#define是宏定义,不论写在什么地⽅,作⽤域是全局
const有⾃⼰的作⽤域
#include <iostream>
using namespace std;
void fun1()
{
#define a 10
const int b = 20;
//#undef a //卸载 a 这个宏常量
//# undef //卸载 所有的宏常量
}
void fun2()
{
cout << "a=" << a << endl; //如果宏常量被卸载了的话,这句话会报错
// printf("b = %d\n", b); //这句话会报错
}
int main()
{
fun1();
fun2(); // a = 10
return 0;
}
3.宏定义时加括号与不加括号是⼀样的
#include <iostream>
#define NUM (7)
#define NUM2 7
int main()
{
if (NUM == NUM2) // true
{
std::cout << "true" << std::endl;
}
else
{
std::cout << "false" << std::endl;
}
return 0;
}
(5)cpp对c的函数增强
【c/c++】cpp对c的函数增强-CSDN博客
cpp中可存在同名函数→函数重载
#include <stdio.h>
void test(int a)
{
printf("%d\n", a);
}
//函数的重载
void test(int a, int b)
{
printf("%d, %d\n", a, b);
}
int main()
{
test(1); // 1 //会自动调用test(int a)
test(3, 2); // 3,2 //会自动调用test(int a, int b)
test(5); // 5 //会自动调用test(int a)
return 0;
}
(6)cpp中函数形参可设置默认值
形参赋值在c中是不合法的
#include <stdio.h>
void test(int a = 0, int b = 0)
{
printf("%d, %d\n", a, b);
}
int main()
{
test(); // 0, 0
test(3, 2); // 3,2
return 0;
}
(7)结构体增强
1.cpp中声明结构体可以更简化
在cpp中创建结构体可以去掉 struct
#include <iostream>
#include <string.h>
struct man
{
char name[20];
int age;
};
int main()
{
// struct man m = {0};
man m = {0};
strcpy(m.name, "tom");
m.age = 20;
std::cout << m.name << "\t" << m.age << std::endl;
return 0;
}
2.结构体的成员函数
cpp结构体中可以有函数,也就是面向对象
// main.cpp
#include <stdio.h>
#include <string.h>
struct man
{
char name[20];
int age;
void set_name(const char *p)
{
strcpy(name, p);
}
void set_age(int a)
{
age = a;
}
const char *get_name()
{
return name;
}
const int get_age()
{
return age;
}
};
int main()
{
//初始化
man m = {0};
//像c一样初始化
strcpy(m.name, "tom");
m.age = 20;
printf("%s, %d\n", m.name, m.age); // tom, 20
//调用方法初始化
char newname[] = "boy";
m.set_name(newname);
m.set_name("girl");
m.set_age(20);
printf("%s, %d\n", m.name, m.age); // girl, 20
//获取属性的值
int myage = m.get_age();
printf("%s, %d\n", m.get_name(), myage); // girl, 20
return 0;
}
3.new、delete和->
// main.cpp
#include <stdio.h>
#include <string.h>
struct man
{
char name[20];
int age;
void set_name(const char *p)
{
strcpy(name, p);
}
void set_age(int a)
{
age = a;
}
const char *get_name()
{
return name;
}
const int get_age()
{
return age;
}
};
int main()
{
//声明
man *m = new man;
//初始化
m->set_age(20);
m->set_name("tome");
//查看属性
printf("%s, %d\n", m->name, m->age); // tome, 20
//清空堆内存
delete m;
return 0;
}
4.构造函数
构造函数就是创建结构体对象时就会⾃动调⽤的函数。
// main.cpp
#include <stdio.h>
#include <string.h>
struct man
{
char name[20];
int age;
void set_name(const char *p)
{
strcpy(name, p);
}
void set_age(int a)
{
age = a;
}
const char *get_name()
{
return name;
}
const int get_age()
{
return age;
}
man() //这个是构造函数,名字和结构名字一样,没有返回值
{
printf("hello world\n");
set_name("tome");
set_age(20);
}
};
int main()
{
//声明
man *m = new man;
//查看属性
printf("%s, %d\n", m->name, m->age); // tome, 20
//清空堆内存
delete m;
return 0;
}
5.构造函数的重载
// main.cpp
#include <stdio.h>
#include <string.h>
struct man
{
char name[20];
int age;
void set_name(const char *p)
{
strcpy(name, p);
}
void set_age(int a)
{
age = a;
}
const char *get_name()
{
return name;
}
const int get_age()
{
return age;
}
man() //这个是构造函数,名字和结构名字一样,没有返回值
{
printf("hello world\n");
set_name("tome");
set_age(20);
}
man(const char *p, int a = 0) //构造函数的重载
{
strcpy(name, p);
age = a;
}
};
int main()
{
//声明
man *m = new man;
//查看属性
printf("%s, %d\n", m->name, m->age); // tome, 20
//清空堆内存
delete m;
//构造函数的重载
//明确地调用有const char *的构造函数
man m2("abc", 100);
printf("%s, %d\n", m2.name, m2.age); // abc, 100
man *myman = new man("zhangsan", 20);
printf("%s, %d\n", myman->name, myman->age); // zhangsan, 20
return 0;
}
6.析构函数
在内存中出现的时候,⾃动调⽤构造函数,如果在内存中销毁的时候⾃动调⽤析构函数
构造函数可以有多个,但析构函数只能有⼀个,⽽且没有参数
// main.cpp
#include <stdio.h>
#include <string.h>
struct man
{
char name[20];
int age;
void set_name(const char *p)
{
strcpy(name, p);
}
void set_age(int a)
{
age = a;
}
const char *get_name()
{
return name;
}
const int get_age()
{
return age;
}
man() //这个是构造函数,名字和结构名字一样,没有返回值
{
printf("init man (A)\n");
set_name("tome");
set_age(20);
}
man(const char *p, int a = 0) //构造函数的重载
{
printf("init man (B)\n");
strcpy(name, p);
age = a;
}
~man() //析构函数,名字和结构名字一样,前面有个~符号,在结构体被内存被释放时执行
{
printf("delete man\n");
}
};
int main()
{
printf("----------\n");
man *m = new man; //在堆里 // 会调用构造函数
delete m; // 会调用析构函数
printf("----------\n");
man m1; //在栈里 // 会调用构造函数
printf("----------\n");
man m2("abc", 100); // 会调用构造函数
printf("----------\n");
// 程序结束前,会释放m1和m2的内存
// 还会调用两次析构函数
return 0;
}
运行结果
----------
init man (A)
delete man
----------
init man (A)
----------
init man (B)
----------
delete man
delete man
7.关键字this
this是cpp的关键字,相当于指针,代表对象的地址
// main.cpp
#include <stdio.h>
#include <string.h>
struct A
{
A()
{
printf("init\n");
}
int age;
void set_age(int age)
{
//形参age与对象的属性重名了
//因此在赋值时需要加上this->age
this->age = age; // this是cpp的关键字,代表对象的地址
}
struct A *get_addr()
{
return this; //返回该对象的地址
}
};
int main()
{
A a; // a是一个对象
printf("%p, %p\n", &a, a.get_addr());
// 000000000061FE1C, 000000000061FE1C
// this就是该对象的地址,这里是在栈里,也可以用new分配在堆里
A *p = new A; //分配地址在堆里
printf("%p, %p\n", p, p->get_addr());
//0000000000686EF0, 0000000000686EF0
return 0;
}
8.面向对象的动态分配和释放内存
成员重新赋值时,如果需要重新分配内存,一定先delete之前分配过的,别出现内存泄漏
还要注意指针判空保护
// main.cpp
#include <stdio.h>
#include <string.h>
struct man
{
char *name;
int age;
man()
{
name = NULL;
}
void set_name(const char *name)
{
if (name)
{
// 释放旧内存,避免内存泄漏
delete[] this->name;
}
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
}
const char *get_name()
{
if (name)
return name;
else
return "no name";
}
~man()
{
if (name)
{
delete[] name;
}
}
};
int main()
{
man m;
m.set_name("tom");
printf("%s\n", m.get_name()); // tom
m.set_name("hello");
printf("%s\n", m.get_name()); // hello
return 0;
}
(8)关键字class和public、private
1.基本用法
- 为了与c区分,直接把struct换成class即可,实现面向对象。
- C++⾥⾯,结构体里所有的成员都是公有的,就是外部可以访问的。不写public的部分也是公有的。
- class⾥⾯可以设置哪些是公有成员,哪些是私有成员。只有公有成员才可被外部访问,即设置在public领域内的成员不可被外部访问。不写在public的部分是私有的。
- 构造函数和析构函数必须在公有成员⾥⾯。
- 设置私有成员的好处是我们⾃⼰写的这个类,别⼈⽤时我们可以设计⼀些限制。
示例代码
// main.cpp
#include <stdio.h>
#include <string.h>
class man
{
public: //公有成员,外部可访问
char *name;
int age;
man()
{
name = NULL;
age = 0;
other = 100;
}
void set_name(const char *name)
{
if (name)
{
// 释放旧内存,避免内存泄漏
delete[] this->name;
}
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
}
const char *get_name()
{
if (name)
return name;
else
return "no name";
}
int get_other() //在类内部获取私有成员的值
{
return other;
}
~man()
{
if (name)
{
delete[] name;
}
}
private: //私有成员,外部不可访问
int other;
};
int main()
{
man m;
m.set_name("tom");
printf("%s\n", m.get_name()); // tom
m.set_name("hello");
printf("%s\n", m.get_name()); // hello
// printf("%d\n", m.other); // 这里会报错
// note: declared private here
// 用公有成员设置的函数获取私有成员的值
printf("%d\n", m.get_other()); // 100
return 0;
}
2.只有公有成员才可被外部访问
#include <iostream>
using namespace std;
class student
{
int age;
public:
private:
};
int main()
{
student tom;
tom.age = 10; //会报错 //‘int student::age’是私有的
return 0;
}
3.通过私有属性设置业务限制
举个简单的例子,比如再对对象进行改名字时,为了符合法律监管要求,不运行出现某些特殊字符
// main.cpp
#include <stdio.h>
#include <string.h>
class man
{
private:
char *name;
int age;
public:
man()
{
name = NULL;
age = 0;
}
~man()
{
if (name)
{
delete[] name;
}
}
const char *get_name()
{
return name;
}
int get_age()
{
return age;
}
void set_name(const char *name)
{
// 业务限制
if (strcmp(name, "alibaba") == 0)
{
printf("set name invalid\n");
return;
}
// 业务逻辑
if (this->name)
{
delete[] this->name;
}
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
}
void set_age(int age)
{
this->age = age;
}
};
int main()
{
man m;
m.set_name("tom");
printf("%s\n", m.get_name()); // tom
m.set_name("alibaba"); // set name invalid
printf("%s\n", m.get_name()); // tom
return 0;
}
(9)namespace命名空间
1.namespace是什么
所谓namespace,是指标识符的各种可⻅范围。C++标准程序库中的所有标识符都被定义于⼀个名为std的namespace中。
在C++中,名称(name)可以是符号常量、变量、宏、函数、结构、枚举、类和对象等等。为了避免,在⼤规模程序的设计中,以及在程序员使⽤各种各样的C++库时,这些标识符的命名发 ⽣冲突,标准C++引⼊了关键字namespace(命名空间/名字空间/名称空间/名域),可以更好地控 制标识符的作⽤域。
std是c++标准命名空间,c++标准程序库中的所有标识符都被定义在std中,⽐如标准库中的类 iostream、vector等都定义在该命名空间中,使⽤时要加上using声明(using namespace std) 或using指示(如 std::string,std::vector)。
std和iostream的关系:
- 第⼀个版本的c++⽤的是iostream.h。和格式不⼀样,前者没有后缀,实际上,在你的编译器include⽂件夹 ⾥⾯可以看到,⼆者是两个⽂件,打开⽂件就会发现,⾥⾯的代码是不⼀样的。 后缀为.h的头⽂ 件c++标准已经明确提出不⽀持了,早些的实现将标准库功能定义在全局空间⾥,声明在头⽂件⾥(c++标准为了和C区别开,也为了正确使⽤命名空间,规定头⽂件不使⽤后缀.h,当然也可以使用)。当使⽤的时候,该头⽂件没有定义全局命名空间,必须使⽤ using namespace std;这样才能正确使⽤cout。
- using namespace std;这样命名空间std内定义的所有标识符都有效(曝光)。就好像它们被声明为全局变量⼀样。
c与cpp中的命名空间区别
- 在C语⾔中只有⼀个全局作⽤域,C语⾔中所有的全局标识符共享同⼀个作⽤域,标识符之间可能发⽣冲突
- C++中提出了命名空间的概念,不同命名空间中的标识符可以同名⽽不会发⽣冲突,命名空间可以相互嵌套,全局作⽤域也叫默认命名空间
2.标准命名空间的两种用法
第一种(代码更简洁)
#include <iostream>
using namespace std;
int main()
{
cout << "hello world" << endl;
return 0;
}
第二种(显式使用,不会出现标识符同名冲突)
#include <iostream>
int main()
{
std::cout << "hello world" << std::endl;
return 0;
}
3.使用自定义的命名空间
#include <iostream>
#include <string.h>
using namespace std;
// 1.定义命名空间
//在命名空间 namespaceA里定义了 a
namespace spaceA
{
int a = 0;
}
//在命名空间 namespaceB里定义了 a,二者不会冲突
namespace spaceB
{
int a = 100;
namespace spaceC //命名空间的嵌套
{
struct student
{
char name[20];
int age;
};
}
}
int main()
{
// 2.使用自定义的命名空间
// using namespace spaceA;
// using namespace spaceB;
//调用命名空间中定义的变量,用::标明作用域
cout << spaceA::a << "," << spaceB::a << endl;
//使用嵌套命名空间中的Teacher类
// using spaceB::spaceC::student;
// student goodstudent;
spaceB::spaceC::student goodstudent;
goodstudent.age = 1000;
cout << goodstudent.age << endl;
return 0;
}
(10)register关键字增强
register关键字:请求编译器让变量a直接放在寄存器⾥⾯,速度快。
在c语⾔中 register修饰的变量 不能取地址,但是在c++⾥⾯做了调整。
- C++编译器有⾃⼰的优化⽅式,不使⽤register也可能做优化
- C++中可以取得register变量的地址
- C++编译器发现程序中需要取register变量的地址时,register对变量的声明变得⽆效
以下代码在cpp中可运⾏,在c中不⾏
#include <stdio.h>
int main()
{
register int a = 0;
printf("%p\n", &a);
// 在c中会报错:要求寄存器变量‘a’的地址。
return 0;
}
(11)新增bool类型关键字
- C++中的布尔类型,C++在C语⾔的基本类型系统之上增加了bool。
- 理论上bool只占⽤⼀个字节。如果多个bool变量定义在⼀起,可能会各占⼀个bit,这取决于编译器的实现。
- true代表真值,编译器内部⽤1来表示。false代表⾮真值,编译器内部⽤0来表示。
- bool类型只有true(⾮0)和false(0)两个值。
- bool变量只有1(true)和0(false)两种状态,再次赋值⼀个⾮0会变成1(true),赋值0或者false就 是0(false)。C++编译器会在赋值时将⾮0值转换为true,0值转换为false。
#include <iostream>
using namespace std;
int main()
{
//bool值只有0和1
bool a = true; //全小写
bool a1 = false; //
bool b = 1;
bool c = 2;
bool d = -5;
bool e = 0;
cout << a << a1 << b << c << d << e << endl; // 101110
//bool占用一个字节
int size;
size = sizeof(bool);
cout << size << endl; // 1
if (a == 1)
{
cout << "true = 1" << endl; // true = 1
}
if (b == true)
{
cout << "1 = true" << endl; // 1 = true
}
if (c)
{
cout << "not zero is true" << endl; // not zero is true
}
if (c == 1)
{
cout << "not zero is 1" << endl; // not zero is 1
}
if (e == false)
{
cout << "0 = false" << endl; // 0 = false
}
if (!e)
{
cout << "0 = false" << endl; // 0 = false
}
return 0;
}
(12)三目运算符的增强
- 在c中三⽬运算符只能当右值。
- 在c++中不仅可以右值,还可以左值,说明c++中三⽬运算符会返回一个变量。
- 在C++中, 表达式当右值时,是返回值,是变量的值
- 在C++中, 表达式当左值时,返回的是变量的本身。
C++是如何做到的,这样操作的:*(a < b ? &a : &b ) = 30;
c++编译器帮我们程序员完成了取地址的⼯作。
注意:三⽬运算符可能返回的值中如果有⼀个是常量值,则不能作为左值使⽤。
以下代码在c中会报错,因为表达式返回的是值,而不是变量本身。
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
//返回一个最小数 并且给最小数赋值成3
//三目运算符是一个表达式
// 左值是返回变量
(a < b ? a : b ) = 30; // 将a和b中的较小值赋值为30
printf("a = %d, b = %d\n", a, b); // a = 30, b = 20
// 右值时是返回值
int z = (a < b ? a : b );
printf("z = %d\n", z); // z = 20
return 0;
}
end