由于之前学过C语言、java语言,所以这一版笔记不会把所有内容都记录下来,仅记录学习过程中不太清楚的,或者忘记的。比较基础的就不再进一步记录了。



1.关于字符串的补充

在之前的C语言学习中,已经详细的学习了C的各类数据类型,无论是整型还是浮点型,以及对应的作用范围,这里C++相较于大部分C的数据类型的声明,基本都还可以沿用,但是也多了一些优化。

类似于字符串的命名,我们既可以使用C风格下面的命名方式:

char str1[]="hello world";
  • 1.

也可以使用C++中新增的风格命名方式:

(java中也有的一个关键字 string)

string str1="hello world";
  • 1.

当然这里如果想使用string这个关键字的话,一些版本比较早的IDE会需要引用string 的头部文件。

#include <string>
  • 1.

补充一点知识:bool类型和char类型都是只占用一个字节

2.数据的输入和输出

作用类似于C语言的scanf、print关键字,从键盘中获取数据。关键字是:cin、cout

类似于java中的是


system.out.println()
  • 1.
Scanner scanner = new Scanner(System.in);
  • 1.


语法:cin>>变量、cout<<变量

注意其中的尖括号的朝向。

简单举几个例子,一个整型一个字符串型

int main(){

    /*
    数据的输入
    整型
    */
    int a = 0;
    cout<<"请输入一个数字:"<<endl;
    cin >> a;
    cout<<"您输入的数字为"<<a<<endl;

    /*
    字符串型
    */

    string str1;
     cout<<"请输入你的名字:"<<endl;
     cin >> str1;
     cout<<"你输入的值为"<<str1<<endl;

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

回车输入。

另外需要特别注意的一个类型是bool型,如果键盘输入的bool型真为1,假为0,即便输入的是100最后结果也是1。

非0均为真

    bool flag;
    cout<<"请输入bool值:"<<endl;
    cin >> flag;
    cout<<"你输入的值为"<<flag<<endl;
  • 1.
  • 2.
  • 3.
  • 4.

C++学习笔记(二)_默认参数

3.取模运算

第一点:取模运算,两个相除的除数不可以为0,这个是符合正常的数学逻辑。在linux通过gcc编译时通过了,但是实际运行后是得不到结果的,会报异常:

int main(){
/*
取模运算
两个相除的除数不可以为0,所以做不了取模运算
*/
int a1=10;
int b1=0;
cout<<a1<<"与"<<b1<<"取余结果为:"<<a1 % b1<<endl;
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

编译后运行结果为:

C++学习笔记(二)_默认值_02

第二点:取模运算,两个小数是不可以做取模运算的。这里在linux通过gcc编译时都会报错。

int main(){

/*
两个小数是不可以做取模运算的
*/
double d1=3.15;
double d2=1.1;
cout<<a1<<"与"<<b1<<"取余结果为:"<<d1 % d2<<endl;

} 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

编译报错为:

C++学习笔记(二)_默认参数_03

4.C++中,函数的形式参数列表中的形式参数是可以有默认值的。

以如下为例:

int func (int a,int b=1,int c=1){
    return a+b+c;
}
int main(){
    int num1=35;
    int num2=5;
    int num3 =15;
    int sum1=func(num1);
    int sum2=func(num1,num2,num3);
    cout<<sum1<<endl;
    cout<<sum2<<endl;

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

定义的函数func中为其形参b和c 设定了默认值,如果调用该函数时不传入指定值,则使用默认值,如果传入的指定值,那就使用传入的值来计算。

运行结果:

C++学习笔记(二)_默认参数_04

需要注意的问题1:

需要注意的是:如果形式参数的从某个开始有了默认参数,那么从这个位置往后都必须有默认值

举个例子,在创建一个func2函数,我们给第一个参数a一个默认值,b,c不管。

int func2(int a=3,int b,int c);
int main(){
    int num1=35;
    int num2=5;
    int num3 =15;
    int sum3 = func2(num1,num2,num3);
    cout<< sum3<<endl;
  }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

编译会报错:

C++学习笔记(二)_默认值_05

所以规定里,如果形式参数给默认值,则从这个参数开始后续的必须都要给默认值。

需要注意的问题2:

如果函数声明中有默认参数,函数实现就不能有默认参数。

举例来说:

int func(int a,int b);


int main(){
    cout<< func(1,2)<<endl;
    return 0;
}

int func(int a,int b){
    return a+b;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

通常来说,函数的声明和函数体的实现是分别放在头文件和.cpp文件中的,这里输出结果为3,没问题。

但是如果这样改一下,同时给函数声明和函数体加上默认值:

int func(int a =2,int b=4);


int main(){
    cout<< func(1,2)<<endl;
    return 0;
}

int func(int a=2,int b=4){
    return a+b;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

编译就会报错:

C++学习笔记(二)_重载函数_06

修改的时候:需要把声明的默认值去掉,或者把函数实现的默认值去掉,保留一个即可。

int func(int a ,int b);


int main(){
    cout<< func(1,2)<<endl;
    return 0;
}

int func(int a=2,int b=4){
    return a+b;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

5.函数占位参数

C++中函数的形式参数列表可以有占位参数,用来做占位,调用函数时,必须填补该位置。

下面是一个正常的函数:

void func(int a){

    cout<<"this is a func"<<endl;
}
int main(){

    func(100);


}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

这样是正常编译,也可以正常运行出理想的结果。


C++学习笔记(二)_重载函数_07

我们修改一下func,改为:

void func(int a,int){

    cout<<"this is a func"<<endl;
}
int main(){

    func(100);


}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

再次编译,看一下对不对:

C++学习笔记(二)_默认参数_08

会发现这时候编译错误了,显示缺少了一个参数,由此我们可以得到结果:占位的参数也必须传递

void func(int a,int){

    cout<<"this is a func"<<endl;
}
int main(){

    func(100,200);


}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

6.函数重载

函数名可以相通,参数不同,以提高复用性。

函数重载的满足条件:

1.同一个作用域下。

2.函数名相同。

3.函数参数不同(类型不同,或者个数不同,或者顺序不同)


注意:函数的返回值不可以作为函数重载的条件

举个简单的例子

/*
函数重载
*/

//函数名相同,一个int类型参数
int func(int a);
//两个int类型参数,一个占位
int func(int a,int );
//一个double类型占位参数。
int func(double);

int main(){
    func(1);
    func(10,20);
    func(3.14);
    return 0;

}
int func(int a){
    cout<<"this is func Overload 1"<<endl;
    return 0;
}
int func(int a,int){
    cout<<"this is func Overload 2"<<endl;
    return 0;
}
int func(double){
    
    cout<<"this is func Overload 3"<<endl;
    return 0;


}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

输出结果:

C++学习笔记(二)_默认参数_09

这样我们可以发现给定的参数不同,其会运行不同的函数体。

需要注意的事项:

有以下两个需要注意的地方:

引用作为重载条件,函数重载碰到函数的默认参数。

1.引用作为重载条件:

/*
重载注意事项
1.引用作为重载条件,函数重载碰到默认参数。

*/ 

void overload(int a){
    cout<<"普通重载函数"<<endl;
}
void overload(int a,int &b){
    cout<<"包含引用参数的重载函数"<<endl;

}
void overload(int a,const int &b){
    cout<<"包含静态修饰的引用参数的重载函数"<<endl;

}

int main(){
    int a=3,b=4;
    const int c=5;
    overload(a);
    overload(a,b);
    overload(a,c);

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.

输出结果:

C++学习笔记(二)_默认值_10

可以看到引用和const修饰的引用,是可以区分出来不同的重载函数的。在调用的时候给定对应的值即可。


碰到默认参数的问题:

/*
函数重载碰到默认参数
*/

void overload1(int a){
    cout<<"重载函数1 a="<<a<<endl;

}
void overload1(int a,int b=15){
    cout<<"重载函数2 a="<<a<<endl;

}

int main(){
    int num1=5;
    overload1(num1);
    overload1(num1);
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

这里我们编译,gcc就会给我报错了:

C++学习笔记(二)_默认值_11

原因是存在函数重载的冲突问题。C++不允许定义两个参数相同或仅通过默认参数不同的重载函数。这是因为调用函数时会产生歧义,编译器无法确定应该调用哪个函数。

那么由此就可以知道,不能以默认参数来区分重载函数。



7.new操作符

C++中利用new 操作符在堆区开辟数据,有程序员手动开辟,手动释放,释放利用操作符delete

基本语法示例:



/*
引用
*/

int * func(){
    int * a=new int(10);
    return a;
}
int main(){
    int *function = func();
    cout<< *function <<endl;


    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

编译输出结果: 

C++学习笔记(二)_重载函数_12

上述示例我们用在堆区创建整型数据,new返回的是数据类型的指针,输出的结果为开辟空间的字节数,并且上述代码并没有释放,不释放这块空间一直存在,所以我们需要加上释放代码。

/*
引用
*/

int * func(){
    int * a=new int(10);
    return a;
}
int main(){


    int *function = func();
    cout<< *function <<endl;
    delete function;
    cout<< *function <<endl;


    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

C++学习笔记(二)_默认参数_13

删除掉之后,我们在此输出一下,可以看到结果已经不是申请的空间大小了。这里就表明空间被释放了,这里访问就是非法访问了。

那如果是创建数组呢?应该如何创建。

void func2(){

    int * arr=new int[10];
    for(int i=0;i<10;i++){
        arr[i]=i+1;
        
    }
    for(int i=0;i<10;i++){
       cout<<arr[i]<<" ";
       if(arr[i]==10)
       cout<<endl;
        
    }
    delete[] arr;
}
int main(){

    func2();

    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

C++学习笔记(二)_重载函数_14

尤其需要注意的是,在之前C语言中关于数组的笔记中,我们知道数组名只是指向第一个地址的位置,这里如果释放数组空间的时候如果仅仅这样来写:

    delete arr;
  • 1.

就可能只释放了首地址的空间,后续的空间其实并没有释放,C++并未定义这个行为,所以为了保证正确性,数组释放时一定要记得带上[];

    delete[] arr;
  • 1.

8.引用

引用也算是C++中一个比较重要的点,作用就是给变量起别名。

/**
引用
*/
void func3(){

int a =10;
int &b=a;

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

这里有一个变量a指向了一个4字节的空间内容为10;b也指向了这个4字节的空间。如果我要修改了b那么a的值是多少?

void func3(){

int a =10;
int &b=a;
cout<<a<<endl;
b=100;
cout<<a<<endl;

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

输出结果是:

C++学习笔记(二)_重载函数_15

那么我们可以发现这里修改了b的值后,变量a的值也被修改了,也就是说,无论是b还是a只是变量名不同,但是指向的内容是相通的,值要修改了这片空间的内容,a和b都会变成修改后的内容。

引用的用途主要集中在:函数参数传递以及函数返回值优化。

1.引用常用于函数参数传递,特别是在传递大型对象或复杂结构时,引用可以避免不必要的拷贝,从而提高性能。

void increment(int& a) {
    a++;
}

int main() {
    int x = 5;
    increment(x);
    std::cout << "x = " << x << std::endl; // 输出 x = 6
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

2.函数可以返回引用,这在返回大型对象时非常有用,可以避免不必要的拷贝

int& getReference(int& a) {
    return a;
}

int main() {
    int x = 5;
    int& ref = getReference(x);
    ref = 10;
    std::cout << "x = " << x << std::endl; // 输出 x = 10
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.


需要注意的是:

1.引用必须在定义时初始化,并且不能更改指向的对象。

2.通过引用访问变量不需要解引用操作符(*)。

3.引用不能为 NULL,它必须始终引用一个有效的对象

另外需要区分和指针的区别:

指针可以在任何时候初始化,并且可以在程序运行过程中更改指向的对象。通过指针访问变量需要解引用操作符 *。指针可以为 NULL,表示它不指向任何对象。

示例:

#include <iostream>

void modifyValue(int& ref) {
    ref = 20; // 修改引用所指向的变量
}

int main() {
    int x = 10;
    std::cout << "Before: " << x << std::endl; // 输出 10
		//实际这里等同于,给x起了个别名ref,然后通过ref去修改这个值同时作用到了变量x。
    modifyValue(x); // 通过引用传递

    std::cout << "After: " << x << std::endl; // 输出 20

    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

在这个示例中,函数 modifyValue 接受一个整型引用,并将其值修改为 20。当 x 被传递给 modifyValue 时,实际上传递的是 x 本身,因此函数内的修改直接影响到 x。


错误示例,假设我现在想要使用引用传参数,同时要返回值引用,写了如下一个函数:

int& func4(int& a, int& b){
    return a+b;
}

int main(){
    int num1=15;
    int num2=16;
    int &sum=func4(num1,num2);

    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

编译就会报错了

C++学习笔记(二)_重载函数_16

但是这个例子是有问题的,问题在于a + b 是一个临时值(临时变量),它在表达式求值结束后就不存在了,不能返回对临时值的引用。如果我们实在想使用引用传递以及返回值引用来实现两个数相加,应该怎么做呢?

void func4(int& a, int& b, int& result) {
    result = a + b; // 将结果存储在 result 引用中
}

int main() {
    int num1 = 15;
    int num2 = 16;
    int sum;
    func4(num1, num2, sum); // 将计算结果存储在 sum 中

    std::cout << "Sum: " << sum << std::endl; // 输出结果
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

可以给一个变量result,用它来存储计算结果。这样引用就不会指向临时变量,就不会报错了。