Вы сделали много ошибок, и ваш код может многое улучшить.
Позвольте мне показать вам меня.
- Заголовок
Поскольку один файл может включать заголовок несколько раз, для предотвращения любого побочного эффекта для каждого заголовочного файла требуется include guard .
// statfun.h
#ifndef __statfun_H__
# define __statfun_H__
# include <vector>
double mean(const std::vector<double>&);
double sd(const std::vector<double>&);
#endif
Кстати, объявление функции может сокращать имя аргумента.
- Ссылки
Вторая ошибка, которую вы сделали, заключается в том, что вы не использовали ссылку . В c++
объект по умолчанию передается по значению.
Примечание: Это похоже на R
, за исключением того, что у него нет семантики copy-on-write
уровня языка, но пользовательский класс может реализовать это, классы, определенные в пространстве имен std
, могут также реализовать это.
Таким образом, чтобы предотвратить дорогостоящее копирование, сделана ссылка .
double mean(const std::vector<double>&);
Здесь я использовал const left-value reference
(const &
), поскольку mean
не изменит переданный вектор.
- Функциональные блоки.
В c++
функция определяется следующим образом:
return_value func_name(type1 arg1 /* , type2 arg2, ... */)
{
// The function body goes here:
}
Итак
// statfun.cpp
// c++11
#include "statfun.h"
#include <cmath>
double mean(const std::vector<double> &v)
{
double sum = 0;
for (auto &each: v)
sum += each;
return sum / v.size();
}
double sd(const std::vector<double> &v)
{
double square_sum_of_difference = 0;
double mean_var = mean(v);
auto len = v.size();
double tmp;
for (auto &each: v) {
tmp = each - mean_var;
square_sum_of_difference += tmp * tmp;
}
return std::sqrt(square_sum_of_difference / (len - 1));
}
- Вычет типа переменной времени компиляции
Как вы могли заметить в приведенном выше коде, я использовал auto len = v.size()
, который является языковой функцией c ++ 11 - auto .
Поскольку c++11
, c++
может определить тип возвращаемых вызовов функций во время компиляции. Таким образом, вместо определения переменной, такой как typename std::vector<double>::size_type len = v.size()
, теперь у нас есть auto len = v.size()
.
- диапазон для петля
Если вы выучили python
, то вы должны знать range-for
. Так как c++11
, c++
также может сделать это:
for (auto &each: v) {
// Loop body
}
, где v
может быть std::vector
или любой другой контейнер в c++
.
- Проверка ошибок ввода-вывода
И последнее, но не менее важное: вы не проверяли, успешно ли выполнено какое-либо из этих IO
, выполненных вами на std::cout
или std::cin
!
Используя std::cout
или std::cin
, вы должны проверять состояние потока по std::cout.fail()
каждый раз после выполнения ввода-вывода или использовать следующий код:
std::cout.exceptions(std::ios_base::failbit | std::ios_base::badbit);
std::cin.exceptions(std::ios_base::failbit | std::ios_base::badbit);
Для выполнения std::cout
и std::cin
бросков при сбое ввода-вывода.
Мне лично нравится не обрабатывать эту ошибку, и пусть исключение завершает работу программы, поскольку вы ничего не можете сделать, чтобы очистить и возобновить поток управления программой.
Ниже приведен последний фрагмент кода:
// lab1.cpp
// c++11
#include "statfun.h"
#include <iostream>
auto get_use_input() -> std::vector<double>
{
std::vector<double> v;
v.reserve(10);
double userInput;
for (int i = 0; i != 10; ++i) {
std::cout << "Please enter the " << i + 1 << " number: ";
std::cin >> userInput;
std::cout << std::endl;
v.push_back(userInput);
}
return v;
}
void print_vec(const std::vector<double> &v)
{
std::cout << "Vector: ";
for (auto &each: v)
std::cout << each << " ";
std::cout << std::endl;
}
int main() {
// Configure std::cout and std::cin to throw if io fails.
std::cout.exceptions(std::ios_base::failbit | std::ios_base::badbit);
std::cin.exceptions(std::ios_base::failbit | std::ios_base::badbit);
/*
* With "-O3" or [c++17, copy elision](https://en.cppreference.com/w/cpp/language/copy_elision),
* the cost of initializing an object using the return value of anther function is nearly zero.
*/
std::vector<double> v = get_use_input();
print_vec(v);
std::cout.precision(3);
std::cout << "mean: " << mean(v) << " sd: " << sd(v) << std::endl;
std::cout.precision(5);
std::cout <<std::scientific << "mean: " << mean(v) << " sd: " << sd(v) << std::endl;
return 0;
}
Для сборки этой программы необходим компилятор c++
, который поддерживает c++11
и передает -std=c++11
компилятору.
PS: Вы также можете использовать -std=c++14
или -std=c++17
.
Простой Makefile
для сборки программы:
cxx = ${CXX}
# The flags
CPPFLAGS := -std=c++11
# The two line below is the flags I used for clang++-8
# CPPFLAGS := -std=c++17 -Ofast -pipe -flto
# LDFLAGS := -flto -pipe -Wl,--icf=all,-O2,--as-needed,--strip-all,--plugin-opt=O3
lab1: lab1.o statfun.o
$(CXX) $(LDFLAGS) $^ -o $@
statfun.o: statfun.h
lab1.o: statfun.h
.PHONY: clean
rm -f lab1.o statfun.o lab