C++细节

C++ 语言细节

构造函数

  • 函数名与类名完全相同
  • 可以重载
  • 可以有形参,也可以没有形参,可以带有默认参数
  • 不能定义返回值类型,也不能有return语句
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
class Clock{
public:
    Clock() {
        second = 0;
        minute = 0;
        hour = 0;
    }
    Clock(int newS, int newM, int newH);
    // 构造函数的重载
    void setTime(int newS,int newM,int newH){
        second = newS;
        minute = newM;
        hour = newH;
    }
    void showTime();
private:
    int second,minute,hour;
};
void Clock::showTime(){
    cout << hour << ":" << minute << ":" << second << endl;
}
Clock::Clock(int newS, int newM, int newH) {
    second = newS;
    minute = newM;
    hour = newH;
}
int main(){
    Clock MyClock;
    MyClock.showTime();
    int second,minute,hour;
    cin >> second >> minute >> hour;
    MyClock.setTime(second,minute,hour);
    MyClock.showTime();
    Clock MyClock2(1, 2, 3);
    MyClock2.showTime();
    return 0;
}

拷贝构造函数

class Point{
public:
    Point(Point &p);
private:
    int x,y;
};
// 拷贝构造函数
Point::Point(Point &p) {
    x = p.x;
    y = p.y;
}
  • 当用类的一个对象去初始化该类的另一个对象的时候

    Point a(1,2);
    Point b(a);  // 用对象a初始化对象b,拷贝构造函数被调用
    Point c = b; // 用对象b给对象c赋值,拷贝构造函数被调用
    
  • 当函数的形参是类的对象,调用函数时进行形实结合的时候

    void f(Point p) {
        // code here
    }
    int main() {
        Point a(1,2);
        f(a);
        return 0;
    }
    
  • 当函数的返回值是类的对象,函数执行完成返回调用者的时候

    Point g() {
        Point a(1,2);
        return a;
    }
    

    在我们没有定义类的构造函数的时候,编译器可以在必要的时候,自动帮我们生成一个隐含的拷贝构造函数,在类的数据成员中有指针类型的时候,这个时候默认的复制构造函数能够实现的只有浅拷贝,要实现正确的复制,就是所谓的深拷贝,就必须重新编写复制构造函数才行。

初始化列表

构造函数(参素列表):成员变量(常量或者参数) {
    // 构造函数的函数体
}
clock::clock (int newS, int newM, int newH) {
    second = newS;
    minute = newM;
    hour = newH;
}
// 初始化列表格式
// 必须写在类的声明中
clock (int newS, int newM, int newH):minute(newM),second(newS),hour(newH) {
    // 如果还需要构造函数干别的事
    // 可以和以前一样在构造函数中写下代码
}

一个类被实例化为一个对象,整个过程分为两段:

  1. 对象的构造和成员变量的初始化,初始化列表就是在此期间发挥作用
  2. 执行构造函数中的操作

类的组合

当创建类的对象时,如果这个类具有内嵌对象成员,那么各个内嵌对象将会首先被自动创建。在创建对象的时候,既要对类中的基本数据类型进行初始化,也要对内嵌对象成员进行初始化

内嵌对象的初始化使用了初始化列表来完成——内嵌对象必须使用初始化列表来实现初始化

class Point{
private:
    int x, y;
public:
    Point(int newX, int newY):x(newX),y(newY) {}
};
class Circle{
private:
    double radius;
    point center;
public:
    Circle(double r, point p):radius(r),center(p) {}
};

当我们创建一个组合类的实例对象的时候,不光是它自己的构造函数的函数体被调用执行,而且还将调用其内嵌对象的构造函数。构造函数的调用顺序如下:

  1. 调用内嵌对象的构造函数,调用顺序按照内嵌对象在组合类中的定义中出现的顺序。需要注意的是,在初始化列表中的内嵌对象顺序,跟内嵌对象构造函数的调用顺序是没有关系的
  2. 执行本类构造函数的函数体

析构函数的调用执行顺序与构造函数正好相反

前向引用声明

当遇到两个类互相引用的情况,如下所示:

class A{
public:
    void function(B b);
};
class B{
public:
    void function2(A a);
};

不管哪个类放在前面,结果都会导致编译错误。要解决这种问题,可以使用前向引用声明。如下所示:

class B;
class A{
public:
    void function(B b);
};
class B{
public:
    void function2(A a);
};

需要注意的是,在提供一个完整的类定义之前,不能定义该类的对象,也不能在成员函数中使用该对象

class Elder;
class Toad {
    Elder h; // 错误:类Elder的定义不完善
};
class Elder {
    Toad h;
};

这种情况,可以通过声明前向引用声明类的引用或者指针

class Elder;
class Toad {
    Elder &h;
};
class Elder {
    Toad &h;
};

但是,即使声明了引用,仍然不能在类内的方法定义中直接调用定义不完善的类的方法:

class Elder;
class Toad {
private:
    Elder &h;
public:
    void setWeather() {
        h.sunny(); // 不合法,因为此时h尚未定义完善
    }
};

数组

void func(int a[][4]) {
    // 函数定义
}
int table[3][4]; // 作为参数的数组
func(table);     // 直接使用数组名作为参数
// 使用数组名传递参数的时候,传递的是地址

对象数组

class Point{
    // 类定义
};
Point p[10];
// 在使用对象数组的时候,每个数组元素都是一个对象
int p_x = p[1].x // 这里我们假设x是整形公有成员变量

对象数组的初始化

Point p[2] = {Point(1,2), Point(3,4)}; 
// 假设Point有对应的构造函数
// 也可以只初始化前面的一部分对象元素
Point p[2] = {Point(1,2)};
// 对象数组在初始化的过程中,就会使用带参构造函数初始化p[0],然后用默认构造函数初始化p[1]

数组排序

#include <iostream>
using namespace std;
void sort(int data[], int length) {
    // 数组名的传递int data[]
    for (int i = 0; i < length; i++) {
        for (int j = i - 1; j >= 0; j--) {
            if (data[j] > data[j+1]) {
                swap(data[j], data[j+1]);
            } else {
                break;
            }
        }
    } 
}

int main(){
    int a[10] = {9,8,7,6,5,4,3,2,1,0};
    sort(a,10);
    for (int i = 0; i < 10 ; ++i) {
        cout << a[i] << endl;
    }
    return 0;
}

数组的传递,使用数组类型+数组名+[]

数组与内存

在C++语言中,无论数组内的元素是什么类型的,对一个元素的地址加一个位移值n得到的就是这个元素往后数n后所在元素的地址。一般来说,通过在地址上进行运算的方式,访问数组的效率是比用数组索引的方式执行更快的

int n = 3;
int a[3] = {1,2,3};
int *p;

p = &a[0];
while (n != 0) {
    n--;
    cout << *p << endl;
    p++;
}

字面量

#include <iostream>
using namespace std;
int main() {
    char string[] = "Hello";
    cout << string << endl;
    char *string2 = "Hello";
    cout << string2 << endl;
    cout << &string << endl;
    // string的地址是内存栈区的地址
    cout << static_cast<const void *>(string2) << endl;
    // string2则是直接关联到"Hello"字符串字面量
    // 在内存中字面量池中的地址
    // 不可以通过string2来对字符串做修改
    cout << &"Hello" << endl;
    // "Hello"字符串字面量在内存中字面量池中的地址
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值